How to make Sleep on background thread work with Invoke
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?
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.
As stated on this page:
Thread.Sleep is unique amongst the blocking methods in that suspends Windows message pumping within a Windows Forms application, or COM environment on a thread for which the single-threaded apartment model is used. This is of little consequence with Windows Forms applications, in that any lengthy blocking operation on the main UI thread will make the application unresponsive – and is hence generally avoided – regardless of the whether or not message pumping is "technically" suspended. The situation is more complex in a legacy COM hosting environment, where it can sometimes be desirable to sleep while keeping message pumping alive. Microsoft's Chris Brumme discusses this at length in his web log (search: 'COM "Chris Brumme"').
It seems that Thread.Sleep() in WinForms always pauses the UI, no matter in which thread is called.
精彩评论