使用带有Dispatcher.BeginInvoke的EventWaitHandle进行调度

时间:2018-03-20 05:06:26

标签: c# wpf multithreading event-wait-handle

以下代码片段有两个线程,每个线程将20 string str写入其相应的文本框。完成后,Thread t00会发出信号Thread t01以启动并将共享string str y 更改为 x Thread t00应将20 y 写入文本框,Thread t01应将20 x 写入另一个文本框。相反,Thread t00最终会写出19 y 和1 x 。但是如果我在设置EventWaitHandle之前添加Thread.Sleep(),那就解决了我遇到的问题(我得到20 x 和20 y ),但为什么呢?只有在完成循环后才能设置EventWaitHandle,无论是否有Thread.Sleep()

public partial class MainWindow : Window
{
    public string str = "y";
    static EventWaitHandle _waitHandle = new AutoResetEvent(false);

    public MainWindow()
    {
        InitializeComponent();

        Thread t00 = new Thread(() =>
        {
            for (int i = 0; i < 20; i++)
            {
                Thread.Sleep(200);
                Action action00 = () =>
                {
                    tb00.AppendText(str);
                };
                Dispatcher.BeginInvoke(action00);
            }
            Thread.Sleep(200);  // <-- why this fix the problem??
            _waitHandle.Set();
        });
        t00.Start();


        Thread t01 = new Thread(() =>
        {
            Action action00 = () =>
            {
                tb01.AppendText("Waiting...\n");
            };
            Dispatcher.BeginInvoke(action00);
            _waitHandle.WaitOne();

            str = "x";
            for (int i = 0; i < 20; i++)
            {
                Thread.Sleep(200);
                Action action = () =>
                {
                    tb01.AppendText(str);
                };
                Dispatcher.BeginInvoke(action);
            }
        });
        t01.Start();
    }
}

enter image description here

2 个答案:

答案 0 :(得分:1)

因为您使用的是BeginInvokeBeginInvoke异步调用UI线程上的委托。它向UI线程消息队列发送消息并返回,因此它返回的事实并不意味着实际执行了操作。

出于这个原因,在大多数情况下,当你设置一个等待句柄而另一个线程接收到singal并将stry更改为x时 - 仍然没有调用{{ 1}} UI线程队列中的委托。最终调用时,tb00.AppendText(str);已经str

x“修复”了,因为它为该挂起的委托提供了一些时间在UI线程上执行。使用Thread.Sleep代替Invoke也“修复”了这一点,因为BeginInvoke是同步的,只有在委托实际上已经在UI线程上执行后才会返回。

答案 1 :(得分:1)

考虑BeginInvoke()将一个工作单元推入队列。当从工作队列中弹出第20 action00并执行时,str='x'已经执行,因此当action00实际运行时,它将打印x

Thread.Sleep(200)会在action00执行之前弹出并运行第20 str='x'次,因此会打印y