zoukankan      html  css  js  c++  java
  • 关于 SWT 的UI线程和非UI线程

    要理解UI线程,先要了解一下“消息循环”这个概念。链接是百度百科上的条目,简单地说,操作系统把用户界面上的每个操作都转化成为对应的消息,加入消息队列。然后把消息转发给对应的应用程序(一般来说,就是活动窗口),应用程序根据自己的逻辑处理这些消息。 如果应用程序处理某个消息事件的时候,用了很长的时间,这时候后续的消息无法及时得到处理,就会造成应用程序没有响应,也就是常说的“假死”状态。 所以,应用程序如果处理某个事件需要较长的时间,需要把这个操作放到一个另外的线程中进行处理。 下面再回顾一下前面的简单的SWT程序的结构:

    public static void main(String[] args) {
              Display display = new Display ();
              Shell shell = new Shell (display);
              ......
              shell.open ();
              while (!shell.isDisposed ()) {
                 if (!display.readAndDispatch ()) display.sleep ();
              }
              display.dispose ();
        }

    while循环一段就是处理消息循环的开始,也就是说,一个SWT程序的主线程,就是对应的所谓的UI线程。

    程序中什么地方是UI线程什么地方是非UI线程

    1. 主线程是UI线程
    2. 监听器方法中是UI线程 比如下面这段小程序:
    Label label = new Label (shell, SWT.NONE);
        label.setText ("Enter your name:");
        Text text = new Text (shell, SWT.BORDER);
        text.setLayoutData (new RowData (100, SWT.DEFAULT));
        Button ok = new Button (shell, SWT.PUSH);
        ok.setText ("OK");
        ok.addSelectionListener(new SelectionAdapter() {
            @Override
            public void widgetSelected(SelectionEvent e) {
                while(true) {
                    System.out.println(1);
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e1) {
                        e1.printStackTrace();
                    }
                }
            }
        });

    程序中模拟在点击按钮后,执行一段费时的操作,运行可以看到,程序处于无响应状态:

    uithread.PNG

    避免无响应

    那么,如何避免程序进入无响应状态呢? 其实很简单,不要在UI线程中执行长时间的操作,如果必需要执行费时操作,就在另外的线程中执行:

    ok.addSelectionListener(new SelectionAdapter() {
            @Override
            public void widgetSelected(SelectionEvent e) {
                new Thread() {
                    public void run() {
                        while(true) {
                            System.out.println(1);
                            try {
                                Thread.sleep(1000);
                            } catch (InterruptedException e1) {
                                e1.printStackTrace();
                            }
                        }
                    }
                }.start();
            }
        });

    这样再次执行,可以看到点击按钮后,程序不再会进入无响应状态。

    非UI线程访问UI

    所以对控件的操作都必需在UI线程中进行,否则会抛出线程访问错误,还是以上面代码为例,我们现在改成打印text控件的文本:

    ok.addSelectionListener(new SelectionAdapter() {
            @Override
            public void widgetSelected(SelectionEvent e) {
                // 此处代码直接在监听器方法中,是UI线程
                new Thread() {
                    public void run() {
                        // 此处为另外一个单独线程,非UI线程
                        while(true) {
                            System.out.println(text.getText());
                            try {
                                Thread.sleep(1000);
                            } catch (InterruptedException e1) {
                                e1.printStackTrace();
                            }
                        }
                    }
                }.start();
            }
        });

    运行程序,点击按钮,就会抛出下面的异常:

    Exception in thread "Thread-0" org.eclipse.swt.SWTException: Invalid thread access
        at org.eclipse.swt.SWT.error(SWT.java:4441)
        at org.eclipse.swt.SWT.error(SWT.java:4356)
        at org.eclipse.swt.SWT.error(SWT.java:4327)
        at org.eclipse.swt.widgets.Widget.error(Widget.java:476)
        at org.eclipse.swt.widgets.Widget.checkWidget(Widget.java:367)
        at org.eclipse.swt.widgets.Text.getText(Text.java:1350)
        at test.Snippet108$1$1.run(Snippet108.java:24)

    对于这种在非UI线程访问UI的情况,需要用Display类的syncExec(Runnable)或asyncExec(Runnable)两个方法来执行:

    ok.addSelectionListener(new SelectionAdapter() {
            @Override
            public void widgetSelected(SelectionEvent e) {
                // 此处代码直接在监听器方法中,是UI线程
                new Thread() {
                    public void run() {
                        // 此处为另外一个单独线程,非UI线程 
                        while(true) {
                            // 非UI线程访问UI
                            display.syncExec(new Runnable() {
                                @Override
                                public void run() {
                                    // 这段代码实际上会被放在UI线程中执行
                                    System.out.println(text.getText());
                                }
                            });
                            try {
                                Thread.sleep(1000);
                            } catch (InterruptedException e1) {
                                e1.printStackTrace();
                            }
                        }
                    }
                }.start();
            }
        });

    注意上面的注释说明,syncExec(runnable)方法的参数runnable对象实际上是会被放在UI线程中执行的,所以要注意,不要把Tread.sleep()放到这个runnable里,否则同样会导致界面无响应。

    syncExec和asyncExec方法的区别就是这两个方法一个会等待runnable执行完才返回,asyncExec方法则是立即返回,UI线程会在有空闲的时候去执行runnable。

    那么,是否能够用asyncExec方法执行,同时把上面的sleep放到runnable中呢? 答案依然是不能,原因前面已经提到了,因为runnable实际上是会放到UI线程中执行的,如果这个runnable是非常耗时的,同样会让界面不断陷入每次1秒的无响应状态中, 也相当于新开一个线程执行耗时操作的目的就没有达到了。

  • 相关阅读:
    第三天 moyax
    mkfs.ext3 option
    write file to stroage trigger kernel warning
    download fomat install rootfs script
    custom usb-seriel udev relus for compatible usb-seriel devices using kermit
    Wifi Troughput Test using iperf
    learning uboot switch to standby system using button
    learning uboot support web http function in qca4531 cpu
    learngin uboot design parameter recovery mechanism
    learning uboot auto switch to stanbdy system in qca4531 cpu
  • 原文地址:https://www.cnblogs.com/the-wang/p/7131384.html
Copyright © 2011-2022 走看看