如何让后台线程上的Sleep使用Invoke

时间:2009-10-27 09:53:26

标签: c# multithreading sleep invoke

我在Form上有一个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 loop 在每次迭代后暂停100ms,所以我添加了Thread.Sleep(100);在发射事件之前:

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

然而,这并不是每100毫秒刷新一次pictureBox,而是只绘制循环的最后一次迭代,并且只在循环结束后才会刷新。

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

2 个答案:

答案 0 :(得分:1)

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

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

答案 1 :(得分:1)

正如this page所述:

  

Thread.Sleep在其中是独一无二的   阻止方法暂停   在一个内部抽取Windows消息   Windows窗体应用程序或COM   在一个线程上的环境   单线程公寓模型是   用过的。这没什么影响   使用Windows窗体应用程序   任何冗长的阻塞操作   主UI线程将使   应用程序没有响应 - 并且是   因此通常避免 - 无论如何   消息是否有效   是“技术上”暂停。该   遗产中的情况更为复杂   COM托管环境,它可以在哪里   有时候可能会睡觉   保持信息充满活力。   微软的Chris Brumme讨论   这在他的网络日志中很长(搜索:   'COM“Chris Brumme”')。

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