Warning: file_get_contents(/data/phpspider/zhask/data//catemap/7/arduino/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 使用任务可避免多次调用昂贵的操作并缓存其结果_C#_Multithreading_Task Parallel Library_Async Await_Task - Fatal编程技术网

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

C# 使用任务可避免多次调用昂贵的操作并缓存其结果,c#,multithreading,task-parallel-library,async-await,task,C#,Multithreading,Task Parallel Library,Async Await,Task,我有一个异步方法,可以从数据库中获取一些数据。这个操作相当昂贵,需要很长时间才能完成。因此,我希望缓存该方法的返回值。但是,在异步方法的初始执行有机会返回结果并将其保存到缓存之前,它可能会被多次调用,从而导致多次调用这个昂贵的操作 为了避免这种情况,我目前正在重用一个任务,如下所示: public class DataAccess { private Task<MyData> _getDataTask; public async Task<MyData>

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

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

public class DataAccess
{
    private Task<MyData> _getDataTask;

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

        return await _getDataTask;
    }
}

也许这与结果没有立即枚举有关?

使用锁定功能防止多次调用数据库查询部分。Lock将使它成为线程安全的,因此一旦缓存了它,所有其他调用都将使用它,而不是运行到数据库以实现

lock(StaticObject) // Create a static object so there is only one value defined for this routine
{
    if(_getDataTask == null)
    {
         // Get data code here
    }
    return _getDataTask
}

使用lock函数可以防止多次调用数据库查询部分。Lock将使它成为线程安全的,因此一旦缓存了它,所有其他调用都将使用它,而不是运行到数据库以实现

lock(StaticObject) // Create a static object so there is only one value defined for this routine
{
    if(_getDataTask == null)
    {
         // Get data code here
    }
    return _getDataTask
}

请将函数重写为:

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

    return _getDataTask;
}
公共任务GetDataAsync() { 如果(_getDataTask==null) { _getDataTask=Task.Run(()=>synchronousDataAccessMethod()); } 返回_getDataTask; } 这不应该改变使用此函数可以完成的所有事情-您仍然可以等待返回的任务


如果有任何变化,请告诉我。

请将函数重写为:

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

    return _getDataTask;
}
公共任务GetDataAsync() { 如果(_getDataTask==null) { _getDataTask=Task.Run(()=>synchronousDataAccessMethod()); } 返回_getDataTask; } 这不应该改变使用此函数可以完成的所有事情-您仍然可以等待返回的任务


如果这有什么变化,请告诉我。

如果您想在调用堆栈中进一步等待它,我想您需要:

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;
    }
}
公共类数据访问
{
私有任务_getDataTask;
私有只读对象lockObj=新对象();
公共异步任务GetDataAsync()
{
锁(lockObj)
{
如果(_getDataTask==null)
{
_getDataTask=Task.Run(()=>synchronousDataAccessMethod());
}
}
返回等待getDataTask;
}
}
您的原始代码可能会发生这种情况:

  • 线程1看到
    \u getDataTask==null
    ,并开始构造任务
  • 线程2看到
    \u getDataTask==null
    ,并开始构造任务
  • 线程1完成构建任务,任务启动,线程1等待该任务
  • 线程2完成一个任务的构造,该任务启动,线程2等待该任务

您将得到两个正在运行的任务实例。

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

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;
    }
}
公共类数据访问
{
私有任务_getDataTask;
私有只读对象lockObj=新对象();
公共异步任务GetDataAsync()
{
锁(lockObj)
{
如果(_getDataTask==null)
{
_getDataTask=Task.Run(()=>synchronousDataAccessMethod());
}
}
返回等待getDataTask;
}
}
您的原始代码可能会发生这种情况:

  • 线程1看到
    \u getDataTask==null
    ,并开始构造任务
  • 线程2看到
    \u getDataTask==null
    ,并开始构造任务
  • 线程1完成构建任务,任务启动,线程1等待该任务
  • 线程2完成一个任务的构造,该任务启动,线程2等待该任务

您将得到两个正在运行的任务实例。

回答这个问题有点晚了,但是有一个名为的开源库,它将用两行代码为您完成这项任务,并且它最近被更新以处理这种情况下的缓存任务。它也在nuget上提供

例如:

    Func<Task<List<MyData>>> cacheableAsyncFunc = () => myDataAccessObject.GetDataAsync();

    var cachedData = await cache.GetOrAddAsync("myDataAccessObject.GetData", cacheableAsyncFunc);

    return cachedData;

    // Or instead just do it all in one line if you prefer
    // return await cache.GetOrAddAsync("myDataAccessObject.GetData", myDataAccessObject.GetDataAsync);
}
Func cacheableasync=()=>myDataAccessObject.GetDataAsync();
var cachedData=await cache.GetOrAddAsync(“myDataAccessObject.GetData”,cacheableasync);
返回缓存数据;
//或者,如果你愿意的话,只需在一行中完成
//返回wait cache.GetOrAddAsync(“myDataAccessObject.GetData”,myDataAccessObject.GetDataAsync);
}
默认情况下,它内置了锁定功能,因此每次缓存未命中时,可缓存方法只执行一次,并且它使用lamda,因此您可以一次性执行“获取或添加”。它默认为20分钟滑动到期,但您可以在其上设置任何您喜欢的缓存策略

有关缓存任务的更多信息,请参阅,您可能会发现有用的


(免责声明:我是LazyCache的作者)

回答这个问题有点晚,但是有一个名为的开源库,它将用两行代码为您完成这项工作,并且它最近被更新以处理这种情况下的缓存任务。它也在nuget上提供

例如:

    Func<Task<List<MyData>>> cacheableAsyncFunc = () => myDataAccessObject.GetDataAsync();

    var cachedData = await cache.GetOrAddAsync("myDataAccessObject.GetData", cacheableAsyncFunc);

    return cachedData;

    // Or instead just do it all in one line if you prefer
    // return await cache.GetOrAddAsync("myDataAccessObject.GetData", myDataAccessObject.GetDataAsync);
}
Func cacheableasync=()=>myDataAccessObject.GetDataAsync();
var cachedData=await cache.GetOrAddAsync(“myDataAccessObject.GetData”,cacheableasync);
返回缓存数据;
//或者,如果你愿意的话,只需在一行中完成
//返回wait cache.GetOrAddAsync(“myDataAccessObject.GetData”,myDataAccessObject.GetDataAsync);
}
默认情况下,它内置了锁定功能,因此每次缓存未命中时,可缓存方法只执行一次,并且它使用lamda,因此您可以一次性执行“获取或添加”。它默认为20分钟滑动到期,但您可以在其上设置任何您喜欢的缓存策略

有关缓存任务的更多信息,请参阅,您可能会发现有用的


(免责声明:我是LazyCache的作者)

您只需将
作为