以下代码片段有两个线程,每个线程将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();
}
}
答案 0 :(得分:1)
因为您使用的是BeginInvoke
。 BeginInvoke
异步调用UI线程上的委托。它向UI线程消息队列发送消息并返回,因此它返回的事实并不意味着实际执行了操作。
出于这个原因,在大多数情况下,当你设置一个等待句柄而另一个线程接收到singal并将str
从y
更改为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
。