[英]Does using dispatcher.Invoke make my thread safe?
In my WPF app I have a long running upload running, which raises progress events as it goes which updates a progress bar. 在我的WPF应用程序中,我运行了长时间运行的上载程序,它会在运行过程中引发进度事件,从而更新进度条。 The user also has a chance of cancelling the upload, or it might go wrong.
用户还可以取消上传,否则可能会出错。 These are all async events, so they need to be executed using Dispatcher.Invoke in order to update the UI.
这些都是异步事件,因此需要使用Dispatcher.Invoke执行它们以更新UI。
So the code looks like this, ish: 因此,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;
}
}
Supposing that setting view.Progress on a disposed view is going to throw an error, is this code thread safe? 假设设置视图。在已配置视图上的进度将引发错误,此代码线程安全吗? ie if a user clicks cancel while the progress is updating, he/she will have to wait until the progress has been updated, and if the progress is updated during execution of OnCancelButtonClicked, the Dispatcher.Invoke call will cause the view.Progress update to be queued til after _cancelled is set, so I won't get a problem there.
例如,如果用户在进度更新时单击“取消”,则他/她将不得不等到进度已更新,并且如果在执行OnCancelButtonClicked期间更新了进度,则Dispatcher.Invoke调用将导致view.Progress更新为设置_cancelled之后直到被排队,所以在那里我不会有问题。
Or do I need a lock to be safe, a 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;
}
}
}
You don't have to add a lock. 您不必添加锁。 Dispatcher.Invoke and BeginInvoke requests will not run in the middle of other code (that's the whole point of them).
Dispatcher.Invoke和BeginInvoke请求将不会在其他代码中间运行(这是它们的全部重点)。
Just two things to consider: 仅需考虑两件事:
EDIT: first, I don't have citations because the MSDN pages on the subject are unfortunately very low on details - but I have written test programs to check the behavior of BeginInvoke and everything I write here is the result of those tests. 编辑:首先,我没有被引用,因为不幸的是,关于该主题的MSDN页面的详细信息很低-但是我编写了测试程序来检查BeginInvoke的行为,我在这里编写的所有内容都是这些测试的结果。
Now, to expand on the second point we first need to understand what the dispatcher does. 现在,为了扩展第二点,我们首先需要了解调度程序的功能。 Obviously this is a very simplified explanation.
显然,这是一个非常简化的解释。
Any Windows UI works by processing messages; 任何Windows UI都可以通过处理消息来工作。 For example when the user moves the mouse over a window the system will send that window a WM_MOUSEMOVE message.
例如,当用户将鼠标移到窗口上时,系统将向该窗口发送WM_MOUSEMOVE消息。
The system send the message by adding it a queue, each thread may have a queue, all windows created by the same thread share the same queue. 系统通过将消息添加到队列中来发送消息,每个线程可能有一个队列,同一线程创建的所有窗口共享同一队列。
In the heart of every Windows program there's a loop called "message loop" or "message pump", this loop reads the next message from the queue and calls the appropriate window's code to process that message. 在每个Windows程序的心脏中,都有一个称为“消息循环”或“消息泵”的循环,该循环从队列中读取下一条消息,并调用适当的窗口代码来处理该消息。
In WPF this loop and all the related processing handled by the Dispatcher. 在WPF中,此循环以及分派器处理的所有相关处理。
An application can either be in the message loop waiting for the next message or it could be doing something. 应用程序可以在消息循环中等待下一条消息,也可以在执行某些操作。 That is why when you have a long calculation all the thread's windows become unresponsive - the thread is busy working and doesn't return to the message loop to process the next message.
这就是为什么当您进行长时间的计算时,所有线程的窗口都不响应-线程正在忙于工作,并且不会返回到消息循环来处理下一条消息。
Dispatcher.Invoke and BeginInvoke works by queuing the requested operation and executing it the next time the thread returns to the message loop. Dispatcher.Invoke和BeginInvoke的工作方式是使请求的操作排队并在线程下次返回消息循环时执行该操作。
That is why Dispatcher.(Begin)Invoke can't "inject" code in the middle of your method, you won't get back to the message loop until your method returns. 这就是为什么Dispatcher。(Begin)Invoke不能在方法中间“注入”代码的原因,直到方法返回之前,您不会回到消息循环。
BUT 但
Any code can run a message loop. 任何代码都可以运行消息循环。 When you call anything that runs a message loop the Dispatcher will be called and can run the (Begin)Invoke operations.
当您调用运行消息循环的任何程序时,将调用Dispatcher并可以运行(Begin)Invoke操作。
What kinds of code has a message loop? 哪种代码具有消息循环?
So, to summarize: 因此,总结一下:
This is an interesting question. 这是个有趣的问题。 Items executed in the dispatcher are queued and execute on the same thread as UI interaction.
在调度程序中执行的项目被排队,并在与 UI交互相同的线程上执行 。 Here's the best article on this subject: http://msdn.microsoft.com/en-us/library/ms741870.aspx
这是关于此主题的最佳文章: http : //msdn.microsoft.com/zh-cn/library/ms741870.aspx
If I were to venture a guess, I would say that Dispatcher.Invoke(Action) probably queues an atomic work item , so it's likely going to be ok, however I'm not sure if it wraps up your UI event handler in an atomic action item for instance: 如果我冒险猜测,我会说Dispatcher.Invoke(Action)可能会排队一个原子工作项 ,因此可能没问题,但是我不确定它是否将您的UI事件处理程序包装在原子中动作项目,例如:
//Are these bits atomic? Not sure.
upload.Cancel();
_cancelled = true;
For safety's sake I would personally lock, but your question warrants more research. 为了安全起见,我会亲自锁定,但是您的问题值得进一步研究。 Might need a dive in reflector to figure out for sure.
可能需要潜水反射器才能确定。
As an aside, I'd probably optimize your lock a bit. 顺便说一句,我可能会优化您的锁。
Dispatcher.Invoke(() =>
{
if (!cancelled)
{
lock(myLock)
{
if(!cancelled)
view.Progress = e.Value;
}
}
}
But that's probably overkill :) 但这可能是矫kill过正:)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.