2010-03-23 69 views
2

我有以下方法:使用ThreadPool.QueueUserWorkItem - 線程意外退出

public void PutFile(string ID, Stream content) 
    { 
     try 
     { 
      ThreadPool.QueueUserWorkItem(o => putFileWorker(ID, content)); 
     } 

     catch (Exception ex) 
     { 
      OnPutFileError(this, new ExceptionEventArgs { Exception = ex }); 
     } 
    } 

的putFileWorker方法是這樣的:

private void putFileWorker(string ID, Stream content) 
    { 
     //Get bucket name: 
     var bucketName = getBucketName(ID) 
      .ToLower(); 

     //get file key 
     var fileKey = getFileKey(ID); 

     try 
     { 
      //if the bucket doesn't exist, create it 
      if (!Amazon.S3.Util.AmazonS3Util.DoesS3BucketExist(bucketName, s3client)) 
       s3client.PutBucket(new PutBucketRequest { BucketName = bucketName, BucketRegion = S3Region.EU }); 

      PutObjectRequest request = new PutObjectRequest(); 
      request.WithBucketName(bucketName) 
       .WithKey(fileKey) 
       .WithInputStream(content); 

      S3Response response = s3client.PutObject(request); 
      var xx = response.Headers; 

      OnPutFileCompleted(this, new ValueEventArgs { Value = ID }); 
     } 

     catch (Exception e) 
     { 
      OnPutFileError(this, new ExceptionEventArgs { Exception = e }); 
     } 
    } 

我創建了一個小的控制檯應用程序來測試這一點。 我連接了OnPutFileError和OnPutFileCompleted事件的事件處理程序。

如果我把我的PUTFILE方法,並踏進這一點,它就會以「//如果桶不存在,創建」的路線,然後退出。沒有例外,沒有錯誤,沒有。 它沒有完成(我也在我的事件處理程序中設置了斷點) - 它只是退出。

如果我跑同樣的方法,而不ThreadPool.QueueUserWorkItem那麼它運行良好......

我缺少的東西?

+0

它實際上是停止處理還是在調試環境中獲得線程切換?你的主線程關閉了嗎? (這將Thread.Abort()所有工作線程) – 2010-03-23 18:19:10

+0

以及...它沒有打到上面提到的線後的任何斷點...它只是命中靜態無效Main()方法的結束... – Alex 2010-03-23 18:30:20

+0

我也執行此 - http://statichippo.com/archive/2009/11/09/run-generic-tasks-async-fluent-ly.aspx 這樣做:var async = new AsyncQueueManager(); async.Queue(o => putFileWorker(ID,content));有同樣的結果! – Alex 2010-03-23 18:39:12

回答

8

ThreadPool線程是後臺線程(見鏈接)。如果主線程退出,它們不會讓應用程序繼續運行。

通常,在應用程序的WinForms,這不是一個問題,因爲在主UI線程調用Application.Run,​​並開始處理事件。對於您的控制檯應用程序,如果您的Main方法不等待工作項目以某種方式完成,則主線程將對工作項目排隊,然後退出。

您可以創建一個後臺線程自己及其IsBackground屬性設置爲false。或者您可以創建一個線程並致電Thread.Join等待它完成。

- 編輯 -

如在下面的意見建議,你也可以使用一個ManualResetEvent的,甚至是自定義同步類由Linik的建議。目標是阻止主線程,直到後臺線程完成。

要使用ManualResetEvent的,在你的主線程創建並把它作爲一個參數。 (我會在這裏把它分配給一個靜態變量只是爲了簡單起見。)

ManualResetEvent s_WaitEvent; 

ManualResetEvent s_WaitEvent = new ManualResetEvent(false); // non-signaled 
// queue work item here 
s_WaitEvent.WaitOne(); 

在您的工作線程結束,信號事件:

s_WaitEvent.Set(); 

Link的CountDownLatch是好的,如果你有很多在退出之前必須處理的線程。您也可以爲每個線程使用單獨的ManualResetEvents,並等待它們全部使用WaitHandle.WaitAll(WaitHandle[])完成。 (ManualResetEvent的從WaitHandle的繼承。)

+1

他想要設置'IsBackground = false'來確保它繼續運行。 – 2010-03-23 18:47:25

+0

更正,謝謝。 – 2010-03-23 19:14:07

+0

謝謝,這是發生了什麼。 請您詳細說明IsBackground ...我需要做什麼? – Alex 2010-03-23 20:08:26

1

將一個Console.ReadLine()在你的主線程,而你測試你的工作線程來阻止它。這將使主體不再存在。完成後點擊Enter即可。

+1

Thread.Join更好,因爲它不需要用戶交互。 – 2010-03-23 18:48:30

+0

你不能'Thread.Join()'ThreadPool'中的一個線程,或者至少你不應該這樣做。 – 2010-03-23 18:52:44

+0

這可能適用於玩具代碼,但如果我是客戶,如果程序的結果取決於我何時進入,我將會非常生氣! – 2010-03-23 21:17:35

0

使用的CountDownLatch迫使主要等待所有已排隊線程:

public class CountDownLatch 
{ 
    private int m_remain; 
    private EventWaitHandle m_event; 

    public CountDownLatch (int count) 
    { 
     if (count < 0) 
      throw new ArgumentOutOfRangeException(); 
     m_remain = count; 
     m_event = new ManualResetEvent(false); 
     if (m_remain == 0) 
     { 
      m_event.Set(); 
     } 
    } 

    public void Signal() 
    { 
     // The last thread to signal also sets the event. 
     if (Interlocked.Decrement(ref m_remain) == 0) 
      m_event.Set(); 
    } 

    public void Wait() 
    { 
     m_event.WaitOne(); 
    } 
} 

在主營:

static void Main(string[] args) 
{ 
    CountDownLatch latch = new CountDownLatch(numFiles); 
    // 
    // ... 
    // 
    putFileWorker("blah", streamContent); 
    // 
    // ... 
    // 

    // waits for all of the threads to signal 
    latch.Wait(); 
} 

在輔助方法:

private void putFileWorker(string ID, Stream content) 
{ 
    try 
    { 
     //Get bucket name: 
     var bucketName = getBucketName(ID) 
      .ToLower(); 

     //get file key 
     var fileKey = getFileKey(ID); 

     try 
     { 
      //if the bucket doesn't exist, create it 
      if (!Amazon.S3.Util.AmazonS3Util.DoesS3BucketExist(bucketName, s3client)) 
       s3client.PutBucket(new PutBucketRequest { BucketName = bucketName, BucketRegion = S3Region.EU }); 

      // 
      // ... 
      // 
     } 

     catch (Exception e) 
     { 
      OnPutFileError(this, new ExceptionEventArgs { Exception = e }); 
     } 
    } 
    finally 
    { 
     latch.Signal(); 
    } 
}