在C#中将方法转换为异步的正确方法?

rob*_*rob 7 c# asynchronous async-await

我试图将以下方法(简化示例)转换为异步,因为cacheMissResolver调用在时间方面可能很昂贵(数据库查找,网络调用):

// Synchronous version
public class ThingCache
{
    private static readonly object _lockObj;
    // ... other stuff

    public Thing Get(string key, Func<Thing> cacheMissResolver)
    {
        if (cache.Contains(key))
            return cache[key];

        Thing item;

        lock(_lockObj)
        {
            if (cache.Contains(key))
                return cache[key];

            item = cacheMissResolver();    
            cache.Add(key, item);
        }

        return item;
    }
}
Run Code Online (Sandbox Code Playgroud)

关于消费异步方法有很多关于在线的材料,但我在生产它们时发现的建议似乎不那么明确.鉴于这是打算成为图书馆的一部分,我的尝试是否正确?

// Asynchronous attempts
public class ThingCache
{
    private static readonly SemaphoreSlim _lockObj = new SemaphoreSlim(1);
    // ... other stuff

    // attempt #1
    public async Task<Thing> Get(string key, Func<Thing> cacheMissResolver)
    {
        if (cache.Contains(key))
            return await Task.FromResult(cache[key]);

        Thing item;

        await _lockObj.WaitAsync();

        try
        {
            if (cache.Contains(key))
                return await Task.FromResult(cache[key]);

            item = await Task.Run(cacheMissResolver).ConfigureAwait(false);
            _cache.Add(key, item);
        }
        finally
        {
            _lockObj.Release();
        }

        return item;
    }

    // attempt #2
    public async Task<Thing> Get(string key, Func<Task<Thing>> cacheMissResolver)
    {
        if (cache.Contains(key))
            return await Task.FromResult(cache[key]);

        Thing item;

        await _lockObj.WaitAsync();

        try
        {
            if (cache.Contains(key))
                return await Task.FromResult(cache[key]);

            item = await cacheMissResolver().ConfigureAwait(false);
            _cache.Add(key, item);
        }
        finally
        {
            _lockObj.Release();
        }

        return item;
    }
}
Run Code Online (Sandbox Code Playgroud)

是否使用SemaphoreSlim正确的方法替换异步方法中的锁定语句?(我无法在锁定声明的正文中等待.)

我应该改变cacheMissResolver类型的参数Func<Task<Thing>>吗?虽然这会增加确保解析器func在调用者上是异步的负担(包装Task.Run,我知道如果需要很长时间,它将被卸载到后台线程).

谢谢.

Ser*_*rvy 3

使用 SemaphoreSlim 替换异步方法中的锁定语句的正确方法是吗?

是的。

我应该改为cacheMissResolver类型参数Func<Task<Thing>>吗?

是的。它将允许调用者提供本质上的异步操作(例如 IO),而不是使其仅适合长时间运行的CPU 密集型工作。(同时仍然通过简单地让调用者使用Task.Run自己来支持 CPU 密集型工作,如果这就是他们想要做的。)


除此之外,请注意,将await Task.FromResult(...); 值包装在 a 中Task只是为了立即解开它是没有意义的。这种情况就直接使用结果即可,这种情况下,直接返回缓存的值。你所做的并不是真正的错误,它只是不必要地使代码复杂化/混乱。