[英]C# BackgroundWorker
我有一個按鈕,點擊事件我從網絡獲得一些信息。
當我獲取信息時,我解析它並向ListBox添加項目。 一切都很好,但是當我快速雙擊按鈕時,似乎兩個后台工作人員正在運行,並且在完成所有工作后,列表中的項目被公開。
我想這樣做,如果你單擊按鈕並獲取信息的過程正在工作,這個線程正在停止,只有在第一次工作完成后,第二個工作才開始。
是的,我知道AutoResetEvent,但是當我使用它時,它只幫助了我一次,而且從來沒有更多。 我無法實現這種情況,希望你能幫助我!
現在我甚至嘗試使更容易但沒有成功:(:我添加了一個標志字段(RefreshDialogs)(默認為false),當用戶點擊按鈕時,如果標志為真(這意味着工作正在進行),一切都沒有,但是當flag字段設置為false時,一切都很好,我們開始一個新進程。
當Backgroundwork完成時,我將字段標志更改為false(這意味着用戶可以運行新的過程)。
private void Message_Refresh_Click(object sender, EventArgs e)
{
if (!RefreshDialogs)
{
RefreshDialogs = true;
if (threadBackgroundDialogs.WorkerSupportsCancellation)
{
threadBackgroundDialogs.CancelAsync();
}
if (!threadBackgroundDialogs.IsBusy)
{
downloadedDialogs = 0;
threadBackgroundDialogs = new BackgroundWorker();
threadBackgroundDialogs.WorkerSupportsCancellation = true;
threadBackgroundDialogs.DoWork += LoadDialogs;
threadBackgroundDialogs.RunWorkerCompleted += ProcessCompleted;
threadBackgroundDialogs.RunWorkerAsync();
}
}
}
void ProcessCompleted(object sender, RunWorkerCompletedEventArgs e)
{
RefreshDialogs = false;
}
所以你想在第一個進程運行時保持第二個進程運行,但它們不應該互相干擾? 在第一個完成后,第二個繼續?
粗制方式:循環:
if (!RefreshDialogs)
{
RefreshDialogs = true;
這成了:
while(RefreshDialogs)
{
}
RefreshDialogs = true;
設置為false后,第二個進程將跳出一段時間。 (注意這是極其無效的,因為兩個進程都會一直運行,我很確定第二個進程會阻塞第一個進程,但現在多任務處理它不應該,如果阻塞使用Dispatcher.Thread)
優雅的方式:使用信號量
http://msdn.microsoft.com/de-de/library/system.threading.semaphore%28v=vs.80%29.aspx
如果你發現不可能讓兩個進程同時運行,或者想要另一種方式:添加一個Array / List / int,當第二個進程注意到第一個進程正在運行時,就像你的bool一樣,增加你的Added變量,並在該過程結束時,重新啟動新進程並減少變量:
int number;
if (!RefreshDialogs)
{
RefreshDialogs = true;
your code;
if(number > 0)
{
number--;
restart process
}
}
else
{
number++;
}
我不得不承認,我最喜歡我的最后一個提案,因為它非常高效。
讓你的線程阻塞。 這很容易;
lock(someSharedGlobalObject)
{
Do Work, Exit early if cancelled
}
這樣,其他線程將等到第一個線程釋放鎖定。 它們將永遠不會同時執行並靜默等待,直到它們能夠繼續。
至於其他選擇; 為什么不在單擊時禁用該按鈕,並在后台工作完成時重新啟用它。 唯一的問題是這不允許取消當前線程。 用戶必須等待它完成。 它確實使任何並發很容易消失。
這種方法怎么樣?
創建一個請求隊列或計數器,每按一次按鈕就會遞增一次。 每次計數> 0時,啟動后台工作程序。 當信息到來時,減少計數並檢查0.如果它仍然> 0則重新啟動工作人員。 因為您的請求處理程序變為順序。
在這種方法中,您可能會遇到兩個線程連續引用計數的問題,因為您可以使用鎖定解鎖條件。
我已經為我的應用程序采用了這種方法並且運行良好,希望它對您也一樣。
使用可以使用信號量
class TheClass
{
static SemaphoreSlim _sem = new SemaphoreSlim (3);
static void Main()
{
for (int i = 1; i <= 5; i++)
new Thread (Enter).Start (i);
}
static void Enter (object name)
{
Console.WriteLine (name + " wants to enter");
_sem.Wait();
Console.WriteLine (name + " has entered!");
Thread.Sleep (1000 * (int) name );
Console.WriteLine (name + " is leaving");
_sem.Release(); }
}
}
我不是Windows Phone專家,但我認為它支持TPL,所以下面的代碼可以很好地閱讀:
private object syncRoot =new object();
private Task latestTask;
public void EnqueueAction(System.Action action)
{
lock (syncRoot)
{
if (latestTask == null)
latestTask = Task.Factory.StartNew(action);
else
latestTask = latestTask.ContinueWith(tsk => action());
}
}
我找到了解決方案並感謝@Giedrius。 僅當proccess在最后,當我向Listbox添加項目時,Flag RefreshingDialogs才設置為true。 我使用此標志的原因是,當從網絡獲取內容的異步操作(HttpWebRequest,方法BeginGetRequestStream)開始時,進程狀態發生變化,但是在網絡操作完成后我需要進行UI操作而不僅僅是它們(解析內容並將其添加到Listbox)我的解決方案是:
private object syncRoot = new object();
private Task latestTask;
public void EnqueueAction(System.Action action)
{
lock (syncRoot)
{
if (latestTask == null)
{
downloadedDialogs = 0;
latestTask = Task.Factory.StartNew(action);
}
else if(latestTask.IsCompleted && !RefreshingDialogs)
{
RefreshingDialogs = true;
downloadedDialogs = 0;
latestTask = Task.Factory.StartNew(action);
}
}
}
private void Message_Refresh_Click(object sender, EventArgs e)
{
Action ac = new Action(LoadDialogs2);
EnqueueAction(ac);
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.