Java 尝试异步(线程)显示时出现SWTException
问题:我需要3个线程。Thread1读取一个int值并在该值更改时启动一个操作(涉及UI)。Thread2是UI。Thread3是Thread1注意到int已更改时应执行的操作。现在,当我启动thread2,然后执行Java 尝试异步(线程)显示时出现SWTException,java,multithreading,exception,swt,Java,Multithreading,Exception,Swt,问题:我需要3个线程。Thread1读取一个int值并在该值更改时启动一个操作(涉及UI)。Thread2是UI。Thread3是Thread1注意到int已更改时应执行的操作。现在,当我启动thread2,然后执行 display.async(new Thread1()) 它不会显示UI,因为稍后会调用open()-方法。当我先打开()然后显示.async()时,它会立即抛出一个SWTException: Exception in thread "Thread-0" org.e
display.async(new Thread1())
它不会显示UI,因为稍后会调用open()-方法。当我先打开()然后显示.async()时,它会立即抛出一个SWTException:
Exception in thread "Thread-0" org.eclipse.swt.SWTException: Failed to execute runnable (org.eclipse.swt.SWTException: Invalid thread access)
at org.eclipse.swt.SWT.error(Unknown Source)
at org.eclipse.swt.SWT.error(Unknown Source)
at org.eclipse.swt.widgets.Synchronizer.runAsyncMessages(Unknown Source)
at org.eclipse.swt.widgets.Display.runAsyncMessages(Unknown Source)
at org.eclipse.swt.widgets.Display.readAndDispatch(Unknown Source)
at sokobangui.SokobanGUIManager.run(SokobanGUIManager.java:57)
at java.lang.Thread.run(Unknown Source)
Caused by: org.eclipse.swt.SWTException: Invalid thread access
at org.eclipse.swt.SWT.error(Unknown Source)
at org.eclipse.swt.SWT.error(Unknown Source)
at org.eclipse.swt.SWT.error(Unknown Source)
at org.eclipse.swt.widgets.Display.checkDisplay(Unknown Source)
at org.eclipse.swt.widgets.Display.create(Unknown Source)
at org.eclipse.swt.graphics.Device.(Unknown Source)
at org.eclipse.swt.widgets.Display.(Unknown Source)
at org.eclipse.swt.widgets.Display.(Unknown Source)
at sokobangui.SokobanGUIManager.run(SokobanGUIManager.java:35)
at java.lang.Thread.run(Unknown Source)
at org.eclipse.swt.widgets.RunnableLock.run(Unknown Source)
... 5 more
当我分别启动thread1和thread2而不尝试同步它们时,它工作正常,直到我尝试使用thread2初始化thread3。然后我得到这个:
Exception in thread "Thread-1" org.eclipse.swt.SWTException: Invalid thread access
at org.eclipse.swt.SWT.error(Unknown Source)
at org.eclipse.swt.SWT.error(Unknown Source)
at org.eclipse.swt.SWT.error(Unknown Source)
at org.eclipse.swt.widgets.Widget.error(Unknown Source)
at org.eclipse.swt.widgets.Widget.checkWidget(Unknown Source)
at org.eclipse.swt.widgets.Control.getShell(Unknown Source)
at sokobangui.GUICoordinator.switchToRoboControl(GUICoordinator.java:120)
at sokobangui.GUIListener.run(GUIListener.java:15)
at java.lang.Thread.run(Unknown Source)
因此,如果有人问我这里发生了什么,我会说同步线程有一些问题,因为据我所知,显示线程需要“知道”哪些线程将修改曲面。但是我不知道如何解决这个问题
下面是一些代码片段:(thread1=GUIListener;thread2=SokobanGUIManager;thread3=SwitchToRoboControl)
根本不要启动Gulistener。使用“机器人解谜”的菜单侦听器,它将安排游戏解谜,然后执行一些UI操作。这可以通过两种方式实现: 1) 在“机器人解谜”听者中直接解决游戏 2) 启动一个线程,该线程计算解决方案,然后使用“Display.asyncexec”(或syncExec)在UI中显示解决方案 请参见如何将侦听器附加到菜单项
希望这会有所帮助。所以,Thread3是Thread1发现状态更改时执行操作的一个,对吗?也许一些代码片段可以更好地解释您的用例。你介意分享吗?我添加了一些片段,希望这有助于理解这个问题。但是你是对的,当且仅当thread1(=Guilister)注意到状态更改时,thread3(=SwitchToRoboControl)应该执行一些在UI上应该可见的操作(基本上移动图标)。Guilister是否执行任何UI操作?我是说动作是同步执行的,不是。它只调用进入GUI Coordinator.switchToRoboControl()的connector.switchToRoboControl(),并启动执行UI操作(或应该执行的)的第三个线程。GUIListener实际上只是读取值并调用该方法。是什么将GUIStatus.status的值更改为GUIStatus.INGAME_ROBOT?好的,到目前为止,这解决了问题,但出现了一个新问题:解决谜题的线程应该在机器人执行每一步后更新UI-方法工作正常,但由于某些原因,UI只有在线程终止并死亡后才会更新,因此UI上的图标“跳”到其目标。。。有什么办法解决这个问题吗?因为这个问题最初是我开始使用线程的原因,但显然没有帮助:-/如果使用解决方案1,那么结果就是UI被阻塞。使用第二种解决方案-启动一个常规线程(通过thread.start())并使用Display.asyncexec/syncExcec从中更新UI。等等,我想我需要正确理解这一点:我执行threadSwitchToRoboControl.start()。在run()中,我需要调用Display.async(SokobanGUIManager)?因此,将GUI与解决方案同步,而不是相反?不,当我这样做时,我会得到更多的SWTExceptions…:无法执行runnable;+线程访问无效:“在org.eclipse.swt.widgets.Display.runAsyncMessages(未知源)在org.eclipse.swt.widgets.Display.readAndDispatch(未知源)”侦听器中创建一个SolverThread,您只需调用thread.start()即可启动它。然后,如果每次需要更新UI,只需在该线程中将更新打包为可运行文件,并使用Display.asyncExec执行即可。每次尝试直接从SolverThread访问UI时,您都会得到“无效线程访问”。
public class StartSokobanSolver {
public static void main(String[] args) {
GUIStatus.status = GUIStatus.MAIN;
Thread manager = new Thread(new SokobanGUIManager());
manager.start();
//Thread listener = new Thread(new GUIListener());
//listener.start();
}
}
public class SokobanGUIManager implements Runnable {
public void run() {
this.display = new Display();
this.display.asyncExec(new Thread(new GUIListener()));
this.shell = new Shell(this.display);
goToMainMenu();
this.shell.open();
while (!this.shell.isDisposed()) {
if (!display.readAndDispatch()) {
display.sleep();
}
}
GUIStatus.status = GUIStatus.END;
}
}
public class GUIListener implements Runnable {
private static GUICoordinator connector;
@Override
public void run() {
while (!Thread.currentThread().isInterrupted()) {
//try{
while(GUIStatus.status != GUIStatus.END) {
System.out.println("GUI-Status: " + GUIStatus.status);
if(GUIStatus.status == GUIStatus.INGAME_ROBOT) {
System.out.println("InGameRobot Start");
connector.switchToRoboControl();
System.out.println("InGameRobot End");
GUIStatus.status = GUIStatus.INGAME_SOLVED;
connector.isSolved();
} else {
System.out.println("Else");
try{ Thread.sleep(5000); } catch(Exception e) {}
}
if(GUIStatus.status == GUIStatus.END) {
Thread.currentThread().interrupt();
}
}
/*} catch(Exception e) {
System.out.println("Ende des Threads : " + e.toString());
Thread.currentThread().interrupt();
Not in the code atm to get the full exception message!
}*/
}
}
protected static void setCoordinator(GUICoordinator c) {
connector = c;
}
}
public class GUICoordinator {
...
protected void switchToRoboControl() {
if(this.roboControl != null) {
Solution solution = new Solution("1u:1l:1d");
Thread roco = new Thread(new SwitchToRoboControl(this.map, this, this.mapArea.getShell(), solution));
Display.getDefault().asyncExec(roco);
roco.start();
System.out.println("Thread started");
} else {
System.out.println("Epic fail");
}
}
}
public class SwitchToRoboControl implements Runnable {
...
public SwitchToRoboControl(Map map, GUICoordinator gui, Shell shell, Solution solution) {
this.map = map;
this.gui = gui;
this.shell = shell;
this.solution = solution;
}
@Override
public void run() {
...action to be performed
}
}