【发布时间】:2020-09-16 12:35:09
【问题描述】:
我目前正在尝试优化一个旧的且编写得很糟糕的类,该类处理大量数据,因此很容易花费数小时来运行一组数据。收集数据已经花费了很多时间,这就是我在这里尝试改进的地方。我知道这是相当臭的代码,但这只是一个测试,如果这甚至可以改进任何东西,所以请专注于这个问题:
我尝试了SemaphoreSlim 和Semaphore 以减少同时运行的任务数量。我的数据集将生成大约 70 个任务,这可能会导致线程不足和整体性能下降。至少事实证明它变得不那么敏感了。因此,我尝试将其同时保持在 5 个任务上,以获得更好的整体吞吐量。
现在,当我尝试等待我的任务进入信号量时,它会阻塞(使用 await 的苗条信号量也会阻塞),但即使信号量未满,它也不会进入。此代码位于异步方法中,作为轻微的上下文提示。
Semaphore throttle = new Semaphore(0, 5);
try
{
foreach (var folder in folders)
{
// Wait in case there are already 5 tasks running to reduce thread starvation
collectionTasks.Add(Task.Run( () =>
{
// ReSharper disable once AccessToDisposedClosure
throttle.WaitOne();
return GetGapProfiles(folder.Value, progress, token);
}, token).ContinueWith(
t =>
{
// ReSharper disable once AccessToDisposedClosure
throttle.Release();
return t.Result;
}, TaskContinuationOptions.None));
}
// When all are loaded concat all results into one collection
await Task.WhenAll(collectionTasks);
}
catch (Exception ex)
{
Log.Error(ex, "Failed to collect profiles.");
}
finally
{
throttle.Dispose();
}
我只是不明白为什么这会阻止并且永远不会进入GetGapProfiles。谁能解释一下?
【问题讨论】:
-
信号量为空时阻塞,而不是满时阻塞。你的一开始是空的,从来没有任何东西可以打电话给
Release() -
顺便说一句,你可能想使用
SemaphoreSlim而不是Semaphore:stackoverflow.com/a/29390802/3181933 如果你这样做,我建议使用WaitAsync而不是Wait。 -
顺便说一句,您可能想要使用 Parallel.ForEach 或 DataFlow,您可以在其中显式配置所需的并行度。
-
啊,等等……
GetGapProfiles正在返回一个任务吗? -
要记住的一点是任务不是线程。启动任务不会启动新线程。任务排队,相关的任务调度程序管理实际执行的任务。因此,我不确定您是否通过尝试手动管理任务的执行来实现总体目标。
标签: c# async-await semaphore