1

我的应用程序有需要访问 UI 的后台线程。想象一个国际象棋程序 (AI) 在棋盘上下棋之前“思考”了几秒钟。当线程运行时,UI 被阻止输入,但仍有输出。

涉及3个线程:

  1. CN1 淡香水
  2. 思考线程,使用 invokeAndBlock,输出有关搜索过程的信息(在 TextField 中),例如当前移动、搜索深度和搜索值
  3. 一个时钟线程,以 Thread.start() 开始,每秒更新一次 White 或 Black (TextFields) 使用的时间

在搜索(invokeAndBlock)期间,可以访问 stopButton 以强制停止搜索(未显示)。

下面是我目前的实现。它有效,我的问题是:这是实现它的正确方法吗?

(我阅读了https://www.codenameone.com/blog/callserially-the-edt-invokeandblock-part-1.html和第 2 部分。)

Form mainForm;
TextField whiteTime, blackTime;     // updated by clock thread
TextField searchInfo;               // updated by think thread
Clock clock;
Move move;

public void start() {
    ...
    mainForm = new Form(...);
    ...
    thinkButton.addActionListener((ActionListener) (ActionEvent evt) -> {
        think();
    });
    mainForm.show();
}

void think() {
    blockUI();                      // disable buttons except stopButton
    clock.start(board.player);      // this thread calls showWhiteTime or showBlackTime every second
    invokeAndBlock(() -> {          // off the EDT
        move = search(board, time); // e.g. for 10 seconds
    });
    clock.stop();
    animateMove(board, move);
    clock.start(board.player);
    freeUI();
}

// search for a move to play
Move search(Board board, int time) {
    ...
    while (time > 0) {
        ...
        showSearchInfo(info);       // called say a few times per second
    }
    return move;
}

void showSearchInfo(String s) {     // access UI off the EDT
    callSerially(() -> {            // callSerially is necessary here
        searchInfo.setText(s);
    });
}

void showWhiteTime(String s) {
    whiteTime.setText(s);           // no callSerially needed, although off the EDT (?)
}

void showBlackTime(String s) {
    blackTime.setText(s);           // no callSerially needed, although off the EDT (?)
}

编辑:think、showWhiteTime 和 showBlackTime 的新版本。

// version 2, replaced invokeAndBlock by Thread.start() and callSerially
void think() {
    blockUI();                      // disable buttons except stopButton
    new Thread(() -> {              // off the EDT
        clock.start(board.player);  // this thread calls showWhiteTime or showBlackTime every second
        move = search(board, time); // e.g. for 10 seconds
        clock.stop();
        callSerially(() -> {
            animateMove(board, move);
            clock.start(board.player);
            freeUI();
        });
    }).start();
}

// version 2, added callSerially
void showWhiteTime(String s) {      // access UI off the EDT
    callSerially(() -> {
        whiteTime.setText(s);
    });
}

// version 2, added callSerially
void showBlackTime(String s) {      // access UI off the EDT
    callSerially(() -> {
        blackTime.setText(s);
    });
}
4

1 回答 1

1

大多数代码都很好,但我会避免你在showWhiteTimeshowBlackTime. EDT 违规可能会突然以奇怪的方式失败,因为您触发了异步操作并且事情可能很快变得令人讨厌。我建议在模拟器中打开 EDT 违规检测工具。

使用时要记住两点invokeAndBlock

  • 它比普通线程慢
  • 在某些情况下它会阻止待处理事件,因此将其作为待处理事件链的一部分是有问题的

第二点很难掌握,也是很多错误的根源,所以值得解释一下。

考虑这段代码:

 buttonA.addActionListener(e -> {
       doStuff();
       invokeAndBlock(...);
       doOtherStuff();
 });
 buttonA.addActionListener(e -> doSomethingImportant());

这可能看起来不现实,因为您通常不会一个接一个地添加两个单独的侦听器,但这已经足够了,例如,如果一个更改触发另一个更改等。

当前事件处理将在buttonA期间被阻止invokeAndBlock。这意味着doOtherStuff()将等待invokeAndBlock并且也doSomethingImportant()将等待。

如果doSomethingImportant()显示另一个表单,您可能会在按下按钮并执行许多其他操作后出现奇怪的行为,例如年龄突然改变您的表单。

所以你需要非常注意你对invokeAndBlock.

于 2020-06-24T02:13:06.660 回答