簡體   English   中英

C#BackgroundWorker

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM