首页 > 解决方案 > SemaphoreSlim 在发布前处置?

问题描述

在对此堆栈溢出问题的已接受答案的更新中提到

信号量可能在任务完成之前被释放,并且在调用 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. 我不能等待所有线程完成。这种信号量方法还能用吗?

标签: c#task

解决方案


为什么需要任务清单?

能够调用Task.WaitAll以等待所有任务在处理之前完成SemaphoreSlim。如果您不这样做,则可能会SemaphoreSlim在所有任务完成之前ObjectDisposedException处理concurrencySemaphore.Release().

您正在创建messages.Count任务数并SemaphoreSlim接受maxConcurrency(10) 初始请求数。因此,例如,如果messages.Count等于5,则所有五个任务都将在处理tasks之前立即启动并或多或少地添加SemaphoreSlim- 除非您调用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为类的私有字段并实现IDisposable接口。


推荐阅读