如何使后台线程上的 Sleep 与 Invoke 一起使用

发布于 2024-08-08 14:20:15 字数 1491 浏览 3 评论 0原文

我在 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

贱贱哒 2024-08-15 14:20:15

从线程的角度来看,一切看起来都很好。
最有可能的问题依赖于 _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.

雪若未夕 2024-08-15 14:20:15

正如此页面所述:

Thread.Sleep 是独一无二的
暂停的阻塞方法
Windows 消息在一个
Windows 窗体应用程序或 COM
线程上的环境
单线程公寓模型是
用过的。这没什么影响
对于 Windows 窗体应用程序,
任何长时间的阻塞操作
主 UI 线程将使
应用程序无响应 – 并且是
因此通常会避免——无论如何
是否消息泵送
“技术上”被暂停。这
遗产中的情况更为复杂
COM 托管环境,可以
有时需要睡觉
保持消息的活力。
微软的 Chris Brumme 讨论
在他的网络日志中详细说明了这一点(搜索:
'COM“克里斯·布鲁姆”')。

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

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文