我在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。这个预期的行为或我的线程有什么问题吗?
答案 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,无论调用哪个线程。