是否有可能在不同的线程上等待任务完成?

时间:2017-09-17 13:21:01

标签: c# .net multithreading async-await task-parallel-library

我的意思是说,我们有一个异步方法Task<string> CalculateSha256(string s)。是否有可能,来自线程#1和线程#2的调用被执行连续,因为它们被选择同时执行,没有线程#1和#2知道彼此之间的任何事情?例如,我有一个控制器,

public async Task<string> Get(string value)
{
    return await CalculateSha256(value);
}

我想要两个不同的请求来连续访问负责计算的代码块(CalculateSha256)。

1 个答案:

答案 0 :(得分:4)

您需要同步对CalculateSha256方法的访问,一次只有一个线程会执行它。
lock语句does not play nicely with async起,您就可以使用SemaphoreSlim类。

只需将其作为静态字段添加到控制器:

private static readonly SemaphoreSlim _semaphoreSlim = new SemaphoreSlim(1, 1);

在你的动作中使用WaitAsync方法(并且不要忘记在完成后使用finally块释放它):

public async Task<string> Get(string value)
{
    await _semaphoreSlim.WaitAsync();
    try
    {
        return await CalculateSha256(value);
    }
    finally
    { 
        _semaphoreSlim.Release();
    }
}

如果您发现此方法很有用并且您不想重新尝试try finally块,则可以使用受保护的通用帮助程序创建基本控制器类:

protected async Task<T> GetConsecutively(Func<Task<T>> taskFactory)
{
    await _semaphoreSlim.WaitAsync();
    try
    {
        return await taskFactory();
    }
    finally
    { 
        _semaphoreSlim.Release();
    }
}

修改Get方法以将lambda表达式传递给它:

public async Task<string> Get(string value)
{
    return await GetConsecutively(() => CalculateSha256(value));
}

或者使用Stephen Cleary的AsyncLock代替SemaphoreSlim,它支持更接近lock语句的高级API:

private static readonly AsyncLock _asyncLock = new AsyncLock();

public async Task<string> Get(string value)
{
    //Notice the use of using block
    //instead of try and finally 
    using(await _asyncLock.LockAsync())
    {
        return await CalculateSha256(value);
    }
}