我有以下方法:
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 :(得分: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();
}
}