无法在锁中等待,如何确保不会从多个线程访问异步变量和方法?
c#
我有以下代码:
public const int ThreadLimitMax = 128;
private static object setThreadLimitLock = new object();
private static SemaphoreSlim totalThreadLimiter = new SemaphoreSlim(ThreadLimit, ThreadLimitMax);
public static int ThreadLimit { get; private set; } = 128;
public static async Task SetThreadLimit(int max)
{
if (max > ThreadLimitMax)
throw new ArgumentOutOfRangeException(nameof(max), $"Cannot have more than {ThreadLimitMax} threads.");
if (max < 1)
throw new ArgumentOutOfRangeException(nameof(max), $"Cannot have less than 1 threads.");
lock (setThreadLimitLock)
{
int difference = Math.Abs(ThreadLimit - max);
if (max < ThreadLimit)
{
for (int i = 0; i < difference; i++)
{
await totalThreadLimiter.WaitAsync().ConfigureAwait(false);
}
}
else if (max > ThreadLimit)
{
totalThreadLimiter.Release(difference);
}
ThreadLimit = max;
}
}
我正在尝试创建一种方法来修改 totalThreadLimiter 中可用线程的数量。我将最大线程数保留在 ThreadMaxLimit 整数中。
要更改线程数量,我需要确保在更改最大线程的操作完成之前不会访问 ThreadLimit。我还需要确保该方法被阻塞,直到 totalThreadLimiter 完成所有 WaitAsync() 调用。
我怎样才能做到这一点?
回答
lock
是围绕 的辅助 API Monitor
,它是线程绑定的同步原语,这意味着它不适合与 一起使用await
,因为无法保证当您从不完整的异步操作中返回时您将在哪个线程上。
最终,您需要一个异步感知同步原语;最容易获得的是SemaphoreSlim
,它具有WaitAsync()
用于获取锁的API,带有一个try
/finally
调用Release()
.
在问题的代码中,根据代码分支,您要么获取(仅)信号量,要么释放信号量;那几乎肯定是错误的。正确的用法更像是:
await totalThreadLimiter.WaitAsync();
try
{
// some code with "await" here
}
finally
{
totalThreadLimiter.Release();
}