Warning: file_get_contents(/data/phpspider/zhask/data//catemap/7/wcf/4.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# 多个呼叫者等待长时间运行的GET,有没有办法让他们都等待相同的返回?_C#_Wcf_Rest - Fatal编程技术网

C# 多个呼叫者等待长时间运行的GET,有没有办法让他们都等待相同的返回?

C# 多个呼叫者等待长时间运行的GET,有没有办法让他们都等待相同的返回?,c#,wcf,rest,C#,Wcf,Rest,我正在运行WCF REST服务,发现了一点瓶颈。基本上,当前代码可归结为以下内容: private static List<Widget> widgets; public async Task<List<SearchResult>> Search(string term) { if(widgets == null) { // This call takes up to 60 seconds widgets = aw

我正在运行WCF REST服务,发现了一点瓶颈。基本上,当前代码可归结为以下内容:

private static List<Widget> widgets;  

public async Task<List<SearchResult>> Search(string term)
{
    if(widgets == null) {
        // This call takes up to 60 seconds
        widgets = await GetWidgets();
    }
    return SearchUtil.Search(term, widgets);
}
私有静态列表小部件;
公共异步任务搜索(字符串术语)
{
if(widgets==null){
//此呼叫最长需要60秒
widgets=等待GetWidgets();
}
返回SearchUtil.Search(术语、小部件);
}
问题是许多请求可以进入if检查并调用这个长时间运行的操作。相反,我希望任何额外的传入请求基本上等待原始调用完成,并且只对GetWidgets()进行一次调用。我如何才能做到这一点,这样当字典是空的时,我就不再发出许多请求了

作为一个小插曲,是否可以安全地假设此列表/字典将在服务活动的整个时间内保持填充状态?或者它会因为某种原因而变空吗?处理这种情况的最佳方法是什么(我猜是其他缓存机制)


谢谢

如果不提供线程安全的机制来锁定widgets对象,直到第一个请求完成,这是不完全可能的。由于WCF服务是异步的,许多请求可以开始调用
widgets=await GetWidgets()方法

我建议使用简单的锁,例如:

private static List<Widget> widgets;  
static readonly object _widgetsLock = new object();

public async Task<List<SearchResult>> Search(string term)
{
    if(widgets == null) {
        // This call takes up to 60 seconds
        lock(_widgetsLock)
             if(widgets == null)
                  widgets = await GetWidgets();
    }
    return SearchUtil.Search(term, widgets);
}
私有静态列表小部件;
静态只读对象_widgetsLock=new object();
公共异步任务搜索(字符串术语)
{
if(widgets==null){
//此呼叫最长需要60秒
锁(_widgetsLock)
if(widgets==null)
widgets=等待GetWidgets();
}
返回SearchUtil.Search(术语、小部件);
}
只需锁定
\u widgetsLock
对象,该对象将通过lock方法停止所有其他执行,直到释放
锁。现在,一旦锁被释放,如果有一个等待的线程访问该代码,它就会仔细检查
小部件
实际上是否仍然为空(以前的尝试可能已经加载了它)


在应用程序开始处理请求之前,您还可以在应用程序启动时加载小部件。

如果我理解正确,您需要一个永远不会过期的缓存,并在第一次有人请求值时填充它。以一种通用的方式进行构建应该非常简单:

class NonExpiringLazyLoadingCache<T>
{
    private readonly Func<Task<T>> _factory;
    private Task<T> _retrievalTask;
    private readonly object _lockObject = new object();

    public NonExpiringLazyLoadingCache(Func<Task<T>> factory)
    {
        this._factory = factory;
    }

    public async Task<T> GetValue()
    {
        lock (this._lockObject)
            if (this._retrievalTask == null)
                this._retrievalTask = this._factory();

        await this._retrievalTask;
        return this._retrievalTask.Result;
    }
}

至于对象的生存期,则取决于WCF服务的托管位置。只要进程保持活动状态,该值通常会保持不变。对于IIS托管的服务,当应用程序池被回收时,该值将被清除。

不幸的是,锁体中不能有“wait”。我已经找到了一些方法来完成类似的事情。
 private static NonExpiringLazyLoadingCache<List<Widget>> cache = new NonExpiringLazyLoadingCache<List<Widget>>(GetWidgets);
 ...
 var widgets = await cache.GetValue();