在C#中并行运行多个动作

时间:2019-03-20 17:46:45

标签: c# task-parallel-library .net-4.5

我在ASP.Net Web表单页面中具有以下代码,该页面基本上检查高速缓存中是否有某些值,如果没有,则调用获取数据并将其存储在高速缓存中的方法。获取数据的方法如下

ChartRenderingHelper.GenerateBidsStatusCreated(currYear.ToString(), currQuarter.ToString(), currYearType.ToString())

使用EF调用存储的proc,这3个调用全部调用单独的SP。现在,我按顺序进行操作,因此,如果每个SP花费5秒,则总操作需要15。我在考虑使用一些Tasks在其中运行它们并行,但不确定如何更改当前代码来做到这一点。

  bidsCreated.Value = DashboardCacheHelper.IsIncache(bidsCreatedKey, useCaching) ? DashboardCacheHelper.GetFromCache(bidsCreatedKey) : (string)DashboardCacheHelper.SaveCache(bidsCreatedKey, JsonConvert.SerializeObject(ChartRenderingHelper.GenerateBidsStatusCreated(currYear.ToString(), currQuarter.ToString(), currYearType.ToString())), DateTime.Now.AddDays(cacheDays));
            bidsSubmitted.Value = DashboardCacheHelper.IsIncache(bidsSubmittedKey, useCaching) ? DashboardCacheHelper.GetFromCache(bidsSubmittedKey) : (string)DashboardCacheHelper.SaveCache(bidsSubmittedKey, JsonConvert.SerializeObject(ChartRenderingHelper.GenerateBidsStatusSubmitted(currYear.ToString(), currQuarter.ToString(), currYearType.ToString())), DateTime.Now.AddDays(cacheDays));
            bidsClosed.Value = DashboardCacheHelper.IsIncache(bidsClosedKey, useCaching) ? DashboardCacheHelper.GetFromCache(bidsClosedKey) : (string)DashboardCacheHelper.SaveCache(bidsClosedKey, JsonConvert.SerializeObject(ChartRenderingHelper.GenerateBidsStatusClosed(currYear.ToString(), currQuarter.ToString(), currYearType.ToString())), DateTime.Now.AddDays(cacheDays));

如何并行执行这3个作业?使用TPL,我知道我们可以并行运行方法

Parallel.Invoke(() => DoSomeWork(), () => DoSomeOtherWork());

这是推荐的方法吗?如果我说有12条操作需要并行运行,那么使用EF的所有调用SQL存储的Procs都会导致性能问题。

1 个答案:

答案 0 :(得分:5)

您可以使用异步来实现此目的,但是最好是对Entity Framework代码进行异步实现以进行添加和提取。

首先,我通过引入方法GetOrAddAsync对代码进行了重构,以减少重复并使其更具可读性,该方法接受缓存键,即useCaching的布尔值(这是干什么用的? )和ChartRenderingHelper方法的委托。目前尚不清楚currQuartercurrYearType是什么类型。

private async Task<string> GetOrAddAsync(string cacheKey, bool useCaching, int cacheDays, DateTime currYear, ? currQuarter, ? currYearType, Action<string, string, string> cacheFactory)
{
    if(DashboardCacheHelper.IsIncache(cacheKey, useCaching))
    {
        return DashboardCacheHelper.GetFromCache(cacheKey);
    }

    return (string)(await DashboardCacheHelper.SaveCacheAsync(bidsClosedKey, JsonConvert.SerializeObject(cacheFactory(currYear.ToString(), currQuarter.ToString(), currYearType.ToString())), DateTime.Now.AddDays(cacheDays)).ConfigureAwait(false))
}

使用上述方法,您的分配代码将变为

var bidsCreatedCacheTask = GetOrAddAsync(bidsCreatedKey, useCaching, cacheDays, currYear, currQuarter, currYearType, ChartRenderingHelper.GenerateBidsStatusCreated);
var bidsSubmittedCacheTask = GetOrAddAsync(bidsSubmittedKey, useCaching, cacheDays, currYear, currQuarter, currYearType, ChartRenderingHelper.GenerateBidsStatusSubmitted);
var bidsClosedCacheTask = GetOrAddAsync(bidsClosedKey, useCaching, cacheDays, currYear, currQuarter, currYearType, ChartRenderingHelper.GenerateBidsStatusClosed);

await Task.WhenAll(bidsCreatedCacheTask, bidsSubmittedCacheTask, bidsClosedCacheTask).ConfigureAwait(false);

bidsCreated.Value = await bidsCreatedCacheTask;
bidsSubmitted.Value = await bidsSubmittedCacheTask;
bidsClosed.Value = await bidsClosedCacheTask;

要记住的关键一件事是将您的调用方法标记为async,如果它是事件处理程序,那么它将是async void-但请记住async void {{ 3}}以外的任何方法都不是事件处理程序-因为您受事件处理程序限制为void返回类型。

正如LeBigCat在评论中指出的,ConfigureAwait应该与在.NET Framework上运行的异步代码一起使用以防止死锁,请阅读should otherwise be avoided以了解原因。