1

我在表单上有一个 PictureBox 控件,它应该每 100 毫秒绘制一次。

后台线程在循环中执行一些计算,并在每次迭代后触发一个事件。

编辑 (作为对评论的回复)

World world = new World();

void CreateBackgroundThread() {
    Thread backgroundThread = new Thread(world.BackgroundWorkerFunction);
    backgroundThread.Start();
}

 

public class World { 

    void BackgroundWorkerFunction() {
        IPiece piece = PieceFactory.Create();
        for (int i = 0; i < stepsCount; i++) {
            piece.Calculate();
            if (OnPieceStep != null) {
                OnPieceStep(piece);
            }
        }
    }

}

在主窗体内,有一个处理程序,由以下方式设置:

world.OnPieceStep += DrawScreen;

和 Invoke 包装器(因为要绘制的控件是在 UI 线程中创建的)。

void DrawScreen(IPiece piece) {
    this.Invoke(new PieceStep(_drawScreen), piece);
}
void _drawScreen(IPiece piece) {
    drawer.Draw(world.Board, piece);
}

现在,我希望for 循环在每次迭代后暂停 100 毫秒,所以我添加了 Thread.Sleep(100); 在触发事件之前:

for (int i = 0; i < stepsCount; i++) {
    IPiece piece = Calculate();
    if (OnPieceStep != null) {
        Thread.Sleep(100);
        OnPieceStep(piece);
    }
}

但是,这不会每 100 毫秒刷新一次图片框,而是仅绘制循环的最后一次迭代,并且仅在循环完成后绘制。

Thread.Sleep 不应该暂停调用它的线程,而不是 UI 线程吗?   更新:我刚刚尝试在后台线程计算时单击应用程序。程序阻塞(“无响应”),因此 Thread.Sleep 显然是在 UI 线程上调用的。这是预期的行为还是我的线程有问题?

4

2 回答 2

1

从线程预期一切看起来都很好。最可能的问题依赖于 _drawScreen(IPiece piece) 方法。您是否尝试在绘图后刷新/更新窗口?

一些见解: Control.Invoke 方法使用 Win32 SendMessage 函数将作为参数提供的委托作为窗口消息传递。

于 2009-10-27T09:57:22.430 回答
1

本页所述:

Thread.Sleep 在阻塞方法中是独一无二的,它在使用单线程单元模型的线程上暂停 Windows 窗体应用程序或 COM 环境中的 Windows 消息泵送。这对 Windows 窗体应用程序影响不大,因为主 UI 线程上的任何冗长阻塞操作都会使应用程序无响应 - 因此通常可以避免 - 无论消息泵送是否“技术上”暂停。在传统的 COM 托管环境中,情况更为复杂,有时需要在保持消息泵送活动的同时休眠。Microsoft 的 Chris Brumme 在他的网络日志中详细讨论了这个问题(搜索:'COM "Chris Brumme"')。

似乎 WinForms 中的 Thread.Sleep() 总是暂停 UI,无论在哪个线程中调用。

于 2009-11-03T14:52:18.563 回答