c# - 使用任务来避免多次调用昂贵的操作并缓存其结果

标签 c# multithreading task-parallel-library async-await task

我有一个从数据库中获取一些数据的异步方法。此操作相当昂贵,并且需要很长时间才能完成。因此,我想缓存方法的返回值。但是,异步方法可能会在其初始执行有机会返回并将其结果保存到缓存之前被调用多次,从而导致多次调用此昂贵的操作。

为了避免这种情况,我目前正在重用一个 Task,如下所示:

public class DataAccess
{
    private Task<MyData> _getDataTask;

    public async Task<MyData> GetDataAsync()
    {
        if (_getDataTask == null)
        {
            _getDataTask = Task.Run(() => synchronousDataAccessMethod());
        }

        return await _getDataTask;
    }
}

我的想法是,对 GetDataAsync 的初始调用将启动 Task 中的 synchronousDataAccessMethod 方法,以及对该方法的任何后续调用在 Task 完成之前将简单地等待已经运行的 Task,自动避免多次调用 synchronousDataAccessMethod。在私有(private) Task 完成后调用 GetDataAsync 将导致等待 Task,这将立即返回其初始执行的数据。

这似乎可行,但我遇到了一些奇怪的性能问题,我怀疑这些问题可能与此方法有关。具体来说,即使未调用 synchronousDataAccessMethod 调用,在它完成后等待 _getDataTask 也需要几秒钟(并锁定 UI 线程)。

我是否滥用了 async/await?是否有我没有看到的隐藏问题?是否有更好的方法来完成所需的行为?

编辑

我是这样调用这个方法的:

var result = (await myDataAccessObject.GetDataAsync()).ToList();

可能跟结果没有立即枚举有关系?

最佳答案

如果你想在调用堆栈中进一步等待它,我想你想要这个:

public class DataAccess
{
    private Task<MyData> _getDataTask;
    private readonly object lockObj = new Object();

    public async Task<MyData> GetDataAsync()
    {
        lock(lockObj)
        {
            if (_getDataTask == null)
            {
                _getDataTask = Task.Run(() => synchronousDataAccessMethod());
            }
        }
        return await _getDataTask;
    }
}

您的原始代码有可能发生这种情况:

  • 线程 1 看到 _getDataTask == null,并开始构建任务
  • 线程 2 看到 _getDataTask == null,并开始构建任务
  • 线程 1 完成构建任务,任务开始,线程 1 等待该任务
  • 线程 2 完成构建任务,该任务开始,线程 2 等待该任务

您最终运行了两个任务实例。

关于c# - 使用任务来避免多次调用昂贵的操作并缓存其结果,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25797301/

相关文章:

c# - ASP MVC 工作流工具表单逻辑和权限

c# - 调用 Azure ActiveDirectory 时的任务状态 WaitingForActivation

c# - Parallel.Invoke 是否与调用多个 Task.Run 相同?

c# - Android/Java 对应 C#/C++ 方法 GetTickCount() 的方法是什么?

c# - 带左自连接的 NHibernate QueryOver

C# Lambda 表达式速度

c++ - 线程完成后 boost 线程工作对象的重用

java - 线程没有被终止;陷入循环

java - ExecutorService 停止并等待另一个 ExecutorService 在 Java 中完成

c# - 使用Reader Writer Lock创建线程安全列表