无法在锁中等待,如何确保不会从多个线程访问异步变量和方法?



我有以下代码:

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是围绕Monitor的辅助API,这是一个线程绑定的同步原语,这意味着它不适合与await一起使用,因为无法保证当您从不完整的异步操作中返回时,您将在哪个线程上。

最终,您需要一个异步感知同步原语; 最容易获得的是SemaphoreSlim,它具有用于获取锁的WaitAsync()API,以及调用Release()try/finally

在问题中的代码中,根据代码分支,您要么(仅)获取信号量,要么释放信号量;这几乎可以肯定是错误的。正确的用法更像是:

await totalThreadLimiter.WaitAsync();
try
{
// some code with "await" here
}
finally
{
totalThreadLimiter.Release();
}

最新更新