mci*_*321 1 wpf dispatcher thread-safety
在我的WPF应用程序中,我有一个长时间运行的上传,它会引发进度事件,因为它会更新进度条.用户还有机会取消上传,否则可能会出错.这些都是异步事件,因此需要使用Dispatcher.Invoke执行它们以更新UI.
所以代码看起来像这样,ish:
void OnCancelButtonClicked(object sender, EventArgs e)
{
    upload.Cancel();
    _cancelled = true;
    view.Close();
    view.Dispose();
}
void OnProgressReceived(object sender, EventArgs<double> e)
{
    Dispatcher.Invoke(() => 
    {
        if (!cancelled)
            view.Progress = e.Value;
    }
}
假设在处理视图上设置view.Progress会抛出错误,这个代码线程是否安全?即,如果用户在进度更新时单击取消,则他/她将不得不等待进度更新,如果在执行OnCancelButtonClicked期间更新进度,则Dispatcher.Invoke调用将导致view.Progress更新到在_cancelled设置后排队,所以我不会在那里遇到问题.
或者我需要锁才能安全,la:
object myLock = new object();
void OnCancelButtonClicked(object sender, EventArgs e)
{
    lock(myLock)
    {
        upload.Cancel();
        _cancelled = true;
        view.Close();
        view.Dispose();
    }
}
void OnProgressReceived(object sender, EventArgs<double> e)
{
    Dispatcher.Invoke(() => 
    {
        lock(myLock)
        {
            if (!cancelled)
                view.Progress = e.Value;
        }
    }
}
您不必添加锁.Dispatcher.Invoke和BeginInvoke请求不会在其他代码的中间运行(这是它们的全部要点).
只需要考虑两件事:
编辑:首先,我没有引用,因为关于这个问题的MSDN页面的细节很少 - 但我已经编写了测试程序来检查BeginInvoke的行为,我写的所有内容都是这些测试的结果.
现在,为了扩展第二点,我们首先需要了解调度员的作用.显然这是一个非常简化的解释.
任何Windows UI都通过处理消息来工作; 例如,当用户将鼠标移到窗口上时,系统将向该窗口发送WM_MOUSEMOVE消息.
系统通过添加队列来发送消息,每个线程可以有一个队列,同一个线程创建的所有窗口共享同一个队列.
在每个Windows程序的核心都有一个称为"消息循环"或"消息泵"的循环,该循环从队列中读取下一条消息并调用相应窗口的代码来处理该消息.
在WPF中,此循环和Dispatcher处理的所有相关处理.
应用程序可以在消息循环中等待下一条消息,也可以执行某些操作.这就是为什么当你有一个很长的计算时,所有线程的窗口都没有响应 - 线程正忙着工作,并没有返回到消息循环来处理下一条消息.
Dispatcher.Invoke和BeginInvoke通过对请求的操作进行排队并在下一次线程返回消息循环时执行它来工作.
这就是为什么Dispatcher.(Begin)Invoke不能在你的方法中间"注入"代码,在你的方法返回之前你不会回到消息循环.
但
任何代码都可以运行消息循环.当您调用任何运行消息循环的内容时,将调用Dispatcher并运行(Begin)Invoke操作.
什么类型的代码有消息循环?
所以,总结一下: