使用ThreadPool.QueueUserWorkItem - 线程意外退出

时间:2010-03-23 18:09:35

标签: c# multithreading asynchronous

我有以下方法:

    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 的情况下运行相同的方法,那么它运行正常......

我错过了什么吗?

3 个答案:

答案 0 :(得分: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很不错。您还可以为每个线程使用单独的ManualResetEvent,并使用WaitHandle.WaitAll(WaitHandle[])等待它们全部完成。 (ManualResetEvent继承自WaitHandle。)

答案 1 :(得分:1)

在测试工作线程时,在主线程中放置一个Console.ReadLine()来阻止它。这将保持主要不存在。完成后只需按Enter键即可。

答案 2 :(得分:0)

使用CountDownLatch强制main等待你排队的所有线程:

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();
    }
}

在Main:

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();
    }
}