SemaphoreSlim释放前已处置?

时间:2018-08-13 13:18:21

标签: c# task

在对this stack-overflow question的可接受答案的更新中,提到

  

信号量可能在任务完成之前被处理,并且在调用Release()方法时会引发异常,因此在退出using块之前必须等待所有已创建任务的完成

这怎么发生?

我将代码粘贴到这里:

int maxConcurrency=10;
var messages = new List<string>();
using(SemaphoreSlim concurrencySemaphore = new SemaphoreSlim(maxConcurrency))
{
    List<Task> tasks = new List<Task>();
    foreach(var msg in messages)
    {
        concurrencySemaphore.Wait();

        var t = Task.Factory.StartNew(() =>
        {

            try
            {
                 Process(msg);
            }
            finally
            {
                concurrencySemaphore.Release();
            }
        });

        tasks.Add(t);
    }

    Task.WaitAll(tasks.ToArray());
}

所以1.为什么需要任务列表?

然后2.在我的情况下,没有foreach,而是套接字正在随机接受请求并调用Task.Factory.StartNew。我不能等待所有线程完成。可以继续使用这种信号量方法吗?

1 个答案:

答案 0 :(得分:0)

  

为什么需要任务列表?

在处置Task.WaitAll之前,可以调用SemaphoreSlim等待所有任务完成。如果您不这样做,则SemaphoreSlim可能在所有任务完成之前就被处理掉了,然后当其余任务中的任何一个调用ObjectDisposedException时,您将得到一个concurrencySemaphore.Release()

您正在创建messages.Count个任务,而SemaphoreSlim接受maxConcurrency(10)个初始请求。因此,例如,如果messages.Count等于5,则将立即处理所有五个任务,并在tasks被处置之前或多或少地将其添加到SemaphoreSlim中-除非您调用{{ 1}}。

考虑以下示例代码,在该示例中,我已将对Task.WaitAll的调用注释掉,并将对Task.WaitAll的调用替换为对Process的调用:

Thread.Sleep

此代码将为每个任务抛出int maxConcurrency = 10; var messages = new List<string>() { "1", "2", "3" }; using (SemaphoreSlim concurrencySemaphore = new SemaphoreSlim(maxConcurrency)) { //List<Task> tasks = new List<Task>(); foreach (var msg in messages) { concurrencySemaphore.Wait(); var t = Task.Factory.StartNew(() => { try { Thread.Sleep(5000); } finally { concurrencySemaphore.Release(); } }); //tasks.Add(t); } //Task.WaitAll(tasks.ToArray()); }

  

我不能等待所有线程完成。可以继续使用这种信号量方法吗?

如果您的任务访问ObjectDisposedException的任何成员,则在确定所有任务都已完成之前,请勿处置它。如果您不能或不想等待任务完成,则可能应该将SemaphoreSlim定义为类的私有字段并实现SemaphoreSlim接口。