2009-09-22 95 views
4

我有一個相當複雜的多線程Windows服務工作,但我無法弄清楚如何正確清理。下面是一些[僞]代碼來顯示我的。實際的代碼要複雜得多,在這裏複製/粘貼可能太多了。正確處理線程的類

基本上,我有一個類創建一個線程來完成這項工作的請求。當一個新的請求進入Listener時,它將它發送給處理器,處理器創建新的請求並維護請求列表。如果服務停止,我將清除列表中的所有請求。但是,當請求工作完成後,我該如何清理該類的一個實例?

感謝您的幫助!

尼爾森

class Service 
{ 
    Listener listener; 
    Processor processor; 

    OnStart() 
    { 
    processor = new Processor(); 
    listener = new Listener(processor); 
    } 

    OnStop() 
    { 
    listener.Dispose(); 
    processor.Dispose(); 
    } 
} 

class Listener 
{ 
    Thread thread; 
    bool terminate = false; 

    Listener(Processor processor) 
    { 
    thread = new Thread(DoWork); 
    thread.Start(processor); 
    } 

    DoWork(Processor processor) 
    { 
    WaitForConnection(NewConnection); 
    } 

NewConnection(String data) 
{ 
    processor.NewRequest(data); 

    if (terminate) 
     return; 

    WaitForConnection(NewConnection); 
} 

    Dispose() 
    { 
    terminate = true; 
    thread.Join(); 
    } 
} 

class Processor 
{ 
    //I need to maintain this list so that when the service stops I can cleanly close down 
    List<Request> requests = new List<Request>(); 

    NewRequest(string data) 
    { 
    request.Add(new Request(data)); 
    } 

    Dispose() 
    { 
    //Cleanup each request 
    foreach (Request request in requests) 
    { 
     request.Dispose(); 
    } 
    } 
} 

class Request 
{ 
    Thread thread; 
    bool terminate; 

    Request(string data) 
    { 
    while (true) 
    { 
     //Do some work 
     Thread.Sleep(1000); 

     if (doneWorking) 
     break; 

     if (terminate) 
     return; 
    } 

    //We're done. If I return this thread stops. But how do I properly remove this Request instance from the Processor.requests list? 
    } 

    Dispose() 
    { 
    terminate = true; 
    thread.Join(); 
    } 
} 

回答

3

這是一個粗略的草圖:

delegate void CompletedRequest(Request req); 

class Processor : ITrackCompletion 
{ 
    //I need to maintain this list so that when the service stops I can cleanly close down 
    List<Request> requests = new List<Request>(); 

    public void NewRequest(string data) 
    { 
     lock(requests) 
      request.Add(new Request(data), Complete); 
    } 

    public void Complete(Request req) 
    { 
     lock (requests) 
      requests.Remove(req); 
    } 

    public void Dispose() 
    { 
     //Cleanup each request 
     foreach (Request request in requests.ToArray()) 
     { 
      request.Dispose(); 
     } 
    } 
} 

class Request 
{ 
    Thread thread; 
    bool terminate; 

    public Request(string data, CompletedRequest complete) 
    { 
     try 
     { 
      while (true) 
      { 
       //Do some work 
       Thread.Sleep(1000); 

       if (doneWorking) 
        break; 

       if (terminate) 
        return; 
      } 
     } 
     finally 
     { 
      //We're done. If I return this thread stops. But how do I properly remove this Request instance from the Processor.requests list? 
      complete(this); 
     } 
    } 

    void Dispose() 
    { 
     terminate = true; 
     thread.Join(); 
    } 
} 
+0

這基本上是我現在擁有的。感謝您的確認。爲什麼要做requests.ToArray()?是否可以避免鎖定?以任何方式更快/更安全?謝謝。 – 2009-09-22 20:41:45

+0

更安全,性能更好...鎖定是不可能的,因爲其他線程將從我的列表中刪除它,如果我鎖定列表,Join()永遠不會完成。在鎖定期間不要從功能中調出功能是一種最佳做法。此外,我不能簡單地忽略該鎖,因爲該集合將被修改,並且我的foreach循環會得到一個異常。 – 2009-09-22 23:36:30

+0

我在代碼的另一部分獲得了第一手的經驗。我發現這是因爲鎖定()造成的,但是感謝你,我能夠輕鬆解決它。 – 2009-09-28 21:16:40

4

一種可能性是回調傳遞到委託的形式的請求:「當你完成處理,叫我回來告訴我。」然後,只需在請求處理結束時執行回調,並讓它處理清理。

但需要注意的一件事情是:如果您嘗試通過列表處理事件,然後嘗試從另一個線程的列表中刪除項目,則會出現問題。您應該保留一個標誌(以線程安全的方式訪問),並且一旦開始處理列表中的所有內容,請忽略您獲得的任何回調。

+0

是委託依賴於處理器的特定實例嗎?換句話說,如果我創建了多個Processor實例,並且將一個委託傳遞給Request(對於Processor中的一個方法),那麼當我調用委託時,它是否使用具有正確列表的相同實例? 如果是這樣,那是我錯過了。我有方法,委託,但我不能(可能不應該,即使我可以)直接跨類使用委託。 – 2009-09-22 19:52:18

+0

關於線程安全,不會簡單的鎖定()解決問題嗎?如果我嘗試從列表中刪除一個項目並將Dispose()擊敗我,則該foreach不會找到任何項目。反之亦然,該項目將被刪除,然後Dispose()將清理其餘部分。 我會試一試,讓你知道。再次感謝。 – 2009-09-22 19:54:06

+0

你需要鎖定整個'foreach'循環 - 這實際上並沒有多大用處,因爲在完成時你不再關心。例如,您可以準確決定代表的具體內容 - 例如,每個請求都可能具體。匿名方法或lambda表達式在這裏很有幫助。 – 2009-09-22 19:58:17