`

解决SWT中事件响应的线程冲突问题

阅读更多
最近工作中要做一件这样的事情,就是在一个已有的SWT界面上对一些业务层写好的事件做响应,也就是是SWT的组件订阅了某个时间Listener,当事件触发的时候响应的SWT组件会做出一些信息的显示工作。一开始我们的程序是这样写的:我们所期待的那样做出了反应。问题解决了。总结一下也就是 SWT的在对其组件进行操作的时候有一个单线程的约束,直接的赋值方式是行不通的,SWT在*.widget.Display类中提供了两个方法可以间接的在非用户线程的进行图形构件的访问操作,这是通过的syncExec(Runnable)和asyncExec(Runnable)这两个方法去实现的。而方法syncExec()和asyncExec()的区别在于前者要在指定的线程执行结束后才返回,而后者则无论指定的线程是否执行都会立即返回到当前线程。
我们会在一个业务的监听器中将我们需要进行设置(重绘)的SWT组件从其构造函数中传过去:

java 代码
 
  1. ServiceListener serviceListener = new ServiceListener(txtInformation);  


这里做下说明,ServiceListener是我们自己实现的一个实现了javax.management.NotificationListener 接口的Listener ,它必须实现其handleNotification方法,而txtInformation是一个swt.Text的组件。我们想当然的在这个方法中做一些跟SWT有关的操作于是我们将这个Text组件传进去,再在响应事件中对其进行操作。

java 代码
 
  1. public class ServiceListener implements NotificationListener {  
  2.   
  3. public ServiceListener(Text information) {  
  4. // TODO Auto-generated constructor stub  
  5. super();  
  6. txtInformation = information;  
  7. }  
  8.   
  9. Text txtInformation;  
  10.   
  11. public void handleNotification(Notification notification, Object handback) {  
  12.   
  13. ... ....  
  14.   
  15. //比如说我们取到某些值想要把它赋值给我们的SWT组件  
  16.   
  17. String result = "this is a test!";  
  18.   
  19. txtInformation = result;  
  20.   
  21. }  
  22.   
  23. }  


我们期待的是,当我们期待的事件发生后,会触发这个响应事件做出响应,而我们在已有的SWT界面上的TEXT组件的值会发生改变。但事实上事件是触发了,但程序走到 “txtInformation.setText(result); ”这一行的时候无端停住了,并报了个异常。单我们但不跟踪并查看SWT的源代码的时候看到Text组件中的setText方法是这样写的:
java 代码
 
  1. public void setText (String string) { 

  2. checkWidget (); 

  3. if (string == null) error (SWT.ERROR_NULL_ARGUMENT); 

  4. string = Display.withCrLf (string); 

  5. if (hooks (SWT.Verify) || filters (SWT.Verify)) { 

  6. int length = OS.GetWindowTextLength (handle); 

  7. string = verifyText (string, 0, length, null); 

  8. if (string == nullreturn

  9. }  
  10. TCHAR buffer = new TCHAR (getCodePage (), string, true); 

  11. OS.SetWindowText (handle, buffer);  
  12. /* 
  13. * Bug in Windows.  When the widget is multi line 
  14. * text widget, it does not send a WM_COMMAND with 
  15. * control code EN_CHANGE from SetWindowText () to 
  16. * notify the application that the text has changed. 
  17. * The fix is to send the event. 
  18. */  
  19. if ((style & SWT.MULTI) != 0) { 

  20. sendEvent (SWT.Modify);  
  21. // widget could be disposed at this point 



  22. }  


其中checkWidget ();方法引起了我们的注意,进去看看:
java 代码
 
  1. /** 
  2. * Throws an SWTException if the receiver can not 
  3. * be accessed by the caller. This may include both checks on 
  4. * the state of the receiver and more generally on the entire 
  5. * execution context. This method should be called by 
  6. * widget implementors to enforce the standard SWT invariants. 
  7. * 
  8.  
  9. * Currently, it is an error to invoke any method (other than 
  10. isDisposed()) on a widget that has had its 
  11. dispose() method called. It is also an error 
  12. * to call widget methods from any thread that is different 
  13. * from the thread that created the widget. 
  14. * 
  15.  
  16. * In future releases of SWT, there may be more or fewer error 
  17. * checks and exceptions may be thrown for different reasons. 
  18. * 
  19.  
  20. * 
  21. * @exception SWTException 
    •   * 
    •     
    • ERROR_WIDGET_DISPOSED - if the receiver has been disposed
    •  
    • * 
    •     
    • ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver
    •  
    • *
     
  22. */  
  23. protected void checkWidget () { 

  24. Display display = this.display; 

  25. if (display == null) error (SWT.ERROR_WIDGET_DISPOSED); 

  26. if (display.thread != Thread.currentThread ()) error (SWT.ERROR_THREAD_INVALID_ACCESS);
  27.   
  28. if ((state & DISPOSED) != 0) error (SWT.ERROR_WIDGET_DISPOSED); 

  29. }  

看见没有,事实上这里明显的是不能在多个线程中对同一个SWT组件进行操作,从ibm developerworks上了解到:Java语言本身就提供了多线程机制,这种机制对GUI编程来说是不利的,它不能保证图形构件操作的同步与串行化。SWT采用了一种简单而直接的方式去适应本地GUI系统对线程的要求:在SWT中,通常存在一个被称为"用户线程"的唯一线程,只有在这个线程中才能调用对构件或某些图形API的访问操作。如果在非用户线程中程序直接调用这些访问操作,那么SWTExcepiton异常会被抛出。

那么怎么解决这个问题了,按照developerworks提出的方案,我们对我们的之前写的程序稍微做了一些改进:


java 代码
 
  1. public class ServiceListener implements NotificationListener {  
  2.   
  3. public ServiceListener(Text information) {  
  4. // TODO Auto-generated constructor stub  
  5. super();  
  6. txtInformation = information;  
  7. }  
  8.   
  9. Text txtInformation;  
  10.   
  11. public void handleNotification(Notification notification, Object handback) {  
  12.   
  13. ... ....  
  14.   
  15. //比如说我们取到某些值想要把它赋值给我们的SWT组件  
  16.   
  17. String result = "this is a test!";  
  18.   
  19. //以下是新加的:  
  20.   
  21. txtInformation.getDisplay().asyncExec(new Runnable() {  
  22.   
  23. public void run() {  
  24. txtInformation.setText(result);  
  25. }  
  26. });  
  27.   
  28. }  
  29.   
  30. }  

很好程序如我们所期待的那样做出了反应。问题解决了。总结一下也就是 SWT的在对其组件进行操作的时候有一个单线程的约束,直接的赋值方式是行不通的,SWT在*.widget.Display类中提供了两个方法可以间接 的在非用户线程的进行图形构件的访问操作,这是通过的syncExec(Runnable)和asyncExec(Runnable)这两个方法去实现 的。而方法syncExec()和asyncExec()的区别在于前者要在指定的线程执行结束后才返回,而后者则无论指定的线程是否执行都会立即返回到 当前线程。

enjoying!
分享到:
评论
1 楼 owennet 2008-04-21  
很好,很实在!!

相关推荐

Global site tag (gtag.js) - Google Analytics