2009-10-14 61 views
10

我有一個需要大量初始化的對象(在強壯的機器上1-2秒)。雖然一旦初始化,它只需要大約20毫秒來做一個典型的「工作」正確的方式有一個無休止的工作線程?

爲了防止它每次應用程序想要使用它重新初始化(可能是每秒50次在典型的使用中只需幾分鐘),我決定給它一個job que,並讓它在自己的線程上運行,檢查是否有任何工作。然而,我不完全確定如何使一個線程無論運行還是不運行。

這是我到目前爲止,任何批評是值得歡迎的

private void DoWork() 
    { 
     while (true) 
     { 
      if (JobQue.Count > 0) 
      { 
       // do work on JobQue.Dequeue() 
      } 
      else 
      { 
       System.Threading.Thread.Sleep(50); 
      } 
     } 
    } 

左思右想:我想我可能需要殺死這個線程優雅的歐洲工商管理學院讓它永遠運行下去的,所以我想我會加告訴線程結束的作業類型。關於如何結束這樣的線程的任何想法也讚賞。

回答

19

需要lock,所以你可以WaitPulse

while(true) { 
    SomeType item; 
    lock(queue) { 
     while(queue.Count == 0) { 
      Monitor.Wait(queue); // releases lock, waits for a Pulse, 
           // and re-acquires the lock 
     } 
     item = queue.Dequeue(); // we have the lock, and there's data 
    } 
    // process item **outside** of the lock 
} 

與補充,如:

lock(queue) { 
    queue.Enqueue(item); 
    // if the queue was empty, the worker may be waiting - wake it up 
    if(queue.Count == 1) { Monitor.PulseAll(queue); } 
} 

你也可能想看看this question,這限制了大小隊列(如果隊列太滿則阻塞)。

+0

經典隊列,鎖定和脈動。 – 2009-10-14 15:28:30

+0

+1非常好... – 2009-10-14 15:30:24

+0

爲什麼寫一些已經寫好的東西?寶貝,寶貝。 – Will 2009-10-14 15:30:45

2

您需要一個同步原語,如WaitHandle(查看靜態方法)。通過這種方式,您可以「通知」工作線程有工作。它檢查隊列並繼續工作,直到隊列爲空,此時它會等待互斥信號再次發出信號。

使工作項目之一是quit命令也一樣,這樣就可以發出信號的工作線程時,它的時間才能退出線程

1

在大多數情況下,我已經做到了這一點頗爲相似,你怎麼」已經建立 - 但不是在相同的語言。我有一個使用數據結構(使用Python)的優勢,該數據結構將阻塞線程,直到某個項目放入隊列中,從而無需進行睡眠調用。

如果.NET提供這樣的類,我會考慮使用它。線程阻塞比睡眠呼叫旋轉的線程要好得多。

你可以傳遞的工作可以像「null」一樣簡單;如果代碼收到null,它就知道是時候離開並回家了。

1

抓住Parallel Framework。它有一個BlockingCollection<T>,您可以將其用作作業隊列。你將如何使用它:

  1. 創建BlockingCollection < T>將保存您的任務/作業。
  2. 創建一些線程其中有一個永無休止的循環(而(真){//獲取作業從隊列中)
  3. 設置線程去
  4. 添加作業,當他們來到可用的集合

線程將被阻塞,直到項目出現在集合中。不管輪到誰都會得到它(取決於CPU)。我現在正在使用它,它工作得很好。

它還具有依靠MS編寫多線程訪問相同資源的特別討厭的代碼的優點。只要你能讓其他人寫下你應該去做的。當然,假設他們擁有比您更多的技術/測試資源和相結合的經驗。

+0

您的意思是標註爲「此CTP僅用於測試目的」的CTP?不知道這是一個偉大的建議...在4.0,很好 - 但這是測試! – 2009-10-14 15:45:22

1

如果您確實不需要讓線程退出(並且只是希望它不會讓應用程序繼續運行),那麼您可以將Thread.IsBackground設置爲true,並在所有非後臺線程結束時結束。 Will和Marc都有很好的解決方案來處理隊列。

1

我已經實現了一個後臺任務隊列,但沒有使用任何種類的while循環,脈衝或等待,或者實際上完全觸及Thread對象。它似乎工作。 (我的意思是說,它一直在生產環境中,在過去的18個月中每天處理數千個任務而沒有任何意外的行爲。)這是一個包含兩個重要屬性的類,Queue<Task>BackgroundWorker。有三種重要的方法,這裏縮寫:

private void BackgroundWorker_DoWork(object sender, DoWorkEventArgs e) 
{ 
    if (TaskQueue.Count > 0) 
    { 
     TaskQueue[0].Execute(); 
    } 
} 

private void BackgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) 
{ 
    Task t = TaskQueue[0]; 

    lock (TaskQueue) 
    { 
     TaskQueue.Remove(t); 
    } 
    if (TaskQueue.Count > 0 && !BackgroundWorker.IsBusy) 
    { 
     BackgroundWorker.RunWorkerAsync(); 
    } 
} 

public void Enqueue(Task t) 
{ 
    lock (TaskQueue) 
    { 
     TaskQueue.Add(t); 
    } 
    if (!BackgroundWorker.IsBusy) 
    { 
     BackgroundWorker.RunWorkerAsync(); 
    } 
} 

這並不是說沒有等待和脈動。但是這一切都發生在BackgroundWorker之內。只要任務在隊列中被放下,一直運行,直到隊列爲空,然後再返回到休眠狀態,它就會醒來。

我遠離線程專家。如果使用BackgroundWorker會出現這樣的問題,是否有理由惹惱System.Threading

+0

一般來說,我同意BackgroundWorker類通常是管理後臺任務的簡單選擇。在這種情況下,我不得不挑戰你的一些主張。您正在使用線程對象:'lock'關鍵字是'System.Threading.Monitor.Enter()'和'System.Threading.Monitor.Exit()'的語法糖。而不是一個while循環,你的RunWorkerCompleted事件處理程序調用'BackgroundWorker.RunWorkerAsync()'。在這種情況下,我認爲等待/脈衝while循環的邏輯可能更容易遵循。 – 2010-08-19 22:38:09

+0

夠公平的。這可能對我來說更簡單,因爲我沒有經歷過等待/脈搏。 – 2010-08-19 23:41:45