如何使后台线程上的 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 循环 在每次迭代后暂停 100 毫秒,因此我添加了 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 线程吗? 更新:我刚刚尝试在后台线程计算时单击该应用程序。程序阻塞(“没有响应”),因此 Thread.Sleep 显然是在 UI 线程上调用的。这是预期的行为还是我的线程有问题?
I have a PictureBox control on a Form that is supposed to draw something every 100ms.
The background thread performs some calculation in a loop and after every iteration, it fires an event.
Edited (as a reply to a comment):
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);
}
}
}
}
Inside the main form, there is a handler, set by:
world.OnPieceStep += DrawScreen;
and the Invoke wrapper (since the control to be drawn to, is created in the UI thread).
void DrawScreen(IPiece piece) {
this.Invoke(new PieceStep(_drawScreen), piece);
}
void _drawScreen(IPiece piece) {
drawer.Draw(world.Board, piece);
}
Now, I'd like the for loop to pause 100ms after each iteration, so I added Thread.Sleep(100); before firing an event:
for (int i = 0; i < stepsCount; i++) {
IPiece piece = Calculate();
if (OnPieceStep != null) {
Thread.Sleep(100);
OnPieceStep(piece);
}
}
This however does not refresh the pictureBox every 100 ms, but draws only the last iteration of the loop, and only after the loop finishes.
Shouldn't Thread.Sleep pause the thread it is called on, not the UI thread?
Update: I have just tried to click on the app while the background thread calculates. The program blocks ("not responding"), so the Thread.Sleep was obviously called on UI Thread. Is this expected behaviour or something is wrong with my threading?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
从线程的角度来看,一切看起来都很好。
最有可能的问题依赖于 _drawScreen(IPiecepiece) 方法。您是否尝试在绘制后刷新/更新窗口?
一些见解:
Control.Invoke 方法使用 Win32 SendMessage 函数将作为参数提供的委托作为窗口消息传递。
From threading prospective everything looks fine.
Most likely problem relies within _drawScreen(IPiece piece) method. Have you tried to refresh/update the window after drawing?
Some insight:
Control.Invoke method passes delegate supplied as a parameter as a window message using Win32 SendMessage function.
正如此页面所述:
看来 WinForms 中的 Thread.Sleep() 总是暂停 UI,无论在哪个线程中调用。
As stated on this page:
It seems that Thread.Sleep() in WinForms always pauses the UI, no matter in which thread is called.