[英]Does using dispatcher.Invoke make my thread safe?
在我的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;
}
}
假設設置視圖。在已配置視圖上的進度將引發錯誤,此代碼線程安全嗎? 例如,如果用戶在進度更新時單擊“取消”,則他/她將不得不等到進度已更新,並且如果在執行OnCancelButtonClicked期間更新了進度,則Dispatcher.Invoke調用將導致view.Progress更新為設置_cancelled之后直到被排隊,所以在那里我不會有問題。
還是為了安全起見,我需要一把鎖嗎?
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.Invoke和BeginInvoke的工作方式是使請求的操作排隊並在線程下次返回消息循環時執行該操作。
這就是為什么Dispatcher。(Begin)Invoke不能在方法中間“注入”代碼的原因,直到方法返回之前,您不會回到消息循環。
但
任何代碼都可以運行消息循環。 當您調用運行消息循環的任何程序時,將調用Dispatcher並可以運行(Begin)Invoke操作。
哪種代碼具有消息循環?
因此,總結一下:
這是個有趣的問題。 在調度程序中執行的項目被排隊,並在與 UI交互相同的線程上執行 。 這是關於此主題的最佳文章: http : //msdn.microsoft.com/zh-cn/library/ms741870.aspx
如果我冒險猜測,我會說Dispatcher.Invoke(Action)可能會排隊一個原子工作項 ,因此可能沒問題,但是我不確定它是否將您的UI事件處理程序包裝在原子中動作項目,例如:
//Are these bits atomic? Not sure.
upload.Cancel();
_cancelled = true;
為了安全起見,我會親自鎖定,但是您的問題值得進一步研究。 可能需要潛水反射器才能確定。
順便說一句,我可能會優化您的鎖。
Dispatcher.Invoke(() =>
{
if (!cancelled)
{
lock(myLock)
{
if(!cancelled)
view.Progress = e.Value;
}
}
}
但這可能是矯kill過正:)
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.