2010-03-23 70 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(); 
    } 
}