2010-08-10 109 views
4

可以說我有一個類應該爲我生成一些ID(例如GUID)。現在不幸的是,身份證生成是一個有點漫長的過程,如果我需要一百個身份證,那麼我會遇到顯着減速問題。爲了避免這些情況,我保留了一個預生成ID的隊列,當這個隊列開始在它們上面運行時,我使用BackgroundWorker生成新的ID並將它們放入隊列中。但是我遇到了一些問題。目前最大的一個是如何確保在隊列完全耗盡ID的情況下主線程等待BackroundWorker生成並將它們放入隊列中。下面是我現在擁有的代碼。後臺工作人員同步

public class IdGenerator 
{ 
    private Queue<string> mIds = new Queue<string>(); 
    private BackgroundWorker mWorker = new BackgroundWorker(); 
    private static EventWaitHandle mWaitHandle = new AutoResetEvent(false); 

    public IdGenerator() 
    { 
     GenerateIds(); 

     this.mWorker.DoWork += new DoWorkEventHandler(FillQueueWithIds); 
    } 

    private void GenerateIds() 
    { 
     List<string> ids = new List<string>(); 

     for (int i = 0; i < 100; i++) 
     { 
      ids.Add(Guid.NewGuid().ToString()); 
     } 

     lock (this.mIds) 
     { 

      foreach (string id in ids) 
      { 
       this.mIds.Enqueue(id); 
      } 
     }    
    } 

    public string GetId() 
    { 
     string id = string.Empty; 

     lock (this.mIds) 
     { 
      if (this.mIds.Count > 0) 
      { 
       id = this.mIds.Dequeue(); 
      } 

      if (this.mIds.Count < 100) 
      { 
       if (!this.mWorker.IsBusy) 
       { 
        this.mWorker.RunWorkerAsync(); 
       } 
      } 
     } 

     if (this.mIds.Count < 1) 
     { 
      mWaitHandle.WaitOne(); 
     } 

     return id; 
    } 

    void FillQueueWithIds(object sender, DoWorkEventArgs e) 
    { 
     GenerateIds(); 
     mWaitHandle.Set(); 
    } 
} 

顯然它不能正常工作。看起來我在調用WaitOne和Set方法的適當時機方面存在問題。即使工作人員已經完成工作,有時IsBusy屬性也會返回true。


編輯:

它的一個WinForm,我需要使用.NET 2.0

+0

這似乎是一個真實的模擬,這個代碼也發生錯誤嗎?它是WinForms還是WPF或...? – 2010-08-10 12:38:02

+1

這是一個WinForm。上面的代碼是精簡版,但它的工作方式完全相同,並具有相同的錯誤。 – 2010-08-10 14:42:01

回答

3

你的問題是經典的生產者 - 消費者問題。看看http://en.wikipedia.org/wiki/Producer-consumer_problem

一個簡單的解釋是,你將有兩個線程。一個將是生產者(GUID生成器),另一個將是消費者。

通過使用信號量,您將保持這些線程同步。信號量將負責在隊列滿時停止生產者,並在消費者爲空時停止生產者。

這個過程在Wikipedia文章中有很好的解釋,我打賭你可以在互聯網上的c#中找到生產者 - 消費者的基本實現。

+0

感謝您的提示。我會研究它。 – 2010-08-10 14:38:51

1

您的主代碼(推測爲WinForms)在某個時間點調用mWaitHandle.WaitOne()。此時Messagepump被阻塞,Bgw將無法調用其Completed事件。這意味着IsBusy標誌保持爲真:死鎖。

如果DoWork內的代碼引發異常,則可能會出現類似問題。

編輯:

我認爲,你可以通過使用線程池線程更換BGW解決大部分問題。和一個簡單的volatile bool isbusy標誌。

+0

對,所以主線程需要空閒以供工作人員改變其狀態。正如我懷疑... – 2010-08-10 14:36:17

2

與線程同步相關的一些錯誤,請參閱下面更改的代碼。 當您將鎖定同步應用到隊列時,請注意將所有使用的隊列置於鎖定狀態。 我已經改變了GetId方法來探測新ID,如果沒有。

public class IdGenerator 
{ 
    private Queue<string> mIds = new Queue<string>(); 
    private BackgroundWorker mWorker = new BackgroundWorker(); 
    private static EventWaitHandle mWaitHandle = new AutoResetEvent(false); 

    public IdGenerator() 
    { 
     GenerateIds(); 

     this.mWorker.DoWork += new DoWorkEventHandler(FillQueueWithIds); 
    } 

    private void GenerateIds() 
    { 
     List<string> ids = new List<string>(); 

     for (int i = 0; i < 100; i++) 
     { 
      ids.Add(Guid.NewGuid().ToString()); 
     } 

     lock (this.mIds) 
     { 

      foreach (string id in ids) 
      { 
       this.mIds.Enqueue(id); 
      } 
     }    
    } 

    public string GetId() 
    { 
     string id = string.Empty; 
     //Indicates if we need to wait 
     bool needWait = false; 

     do 
     { 
      lock (this.mIds) 
      { 
       if (this.mIds.Count > 0) 
       { 
        id = this.mIds.Dequeue(); 
        return id; 
       } 

       if (this.mIds.Count < 100 && this.mIds.Count > 0) 
       { 
        if (!this.mWorker.IsBusy) 
        { 
         this.mWorker.RunWorkerAsync(); 
        } 
       } 
       else 
       { 
        needWait = true; 
       } 
      } 

      if (needWait) 
      { 
       mWaitHandle.WaitOne(); 
       needWait = false; 
      } 
     } while(true); 

     return id; 
    } 

    void FillQueueWithIds(object sender, DoWorkEventArgs e) 
    { 
     GenerateIds(); 
     mWaitHandle.Set(); 
    } 
} 
+0

好的,指出,謝謝。認爲它仍然不起作用... – 2010-08-10 14:32:40

3

在.NET 4中,你可以使用BlockingCollection<T>和更一般IProducerConsumerCollection<T>

這裏有2項任務,一個加入另回吐,使用它的例子。

http://msdn.microsoft.com/en-us/library/dd997306.aspx

+0

聽起來像我可以使用的東西...如果我不需要堅持.NET 2.0 – 2010-08-10 14:28:46

0

OK,繼承人的最終解決方案,我跟去了。這一個不使用BackgroundWorker,但它工作。感謝指向生產者 - 消費者問題的Edu。我使用MSDN提供的示例位於here