运行 在 C# 中并行执行多个操作
Run multiple actions in parallel in C#
我在 ASP.Net Web 表单页面中有以下代码,它主要检查缓存中是否有某些值,如果不在,它会调用一个方法来获取数据,然后将其存储在缓存中。获取数据的方法如下
ChartRenderingHelper.GenerateBidsStatusCreated(currYear.ToString(), currQuarter.ToString(), currYearType.ToString())
使用 EF 调用存储过程,这 3 个调用都调用单独的 SP。 =24=] 这些是并行的,但不确定如何更改我当前的代码来做到这一点。
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 个操作需要 运行 并行所有调用 SQL 使用 EF 的存储过程,这将是一个性能问题。
您可以使用异步来实现此目的,但是最好使用异步实现您的 Entity Framework 添加和获取代码。
首先,我重构了代码以减少重复并通过引入方法 GetOrAddAsync
使其更具可读性,该方法接受缓存键,useCaching
的布尔值(这是什么for?) 和 ChartRenderingHelper
方法的委托。目前还不清楚 currQuarter
和 currYearType
是什么类型。
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
should otherwise be avoided 在任何不是事件处理程序的方法中 - 因为您被限制为具有事件处理程序的 void
return 类型。
正如 LeBigCat 在评论中指出的那样,ConfigureAwait
应该与在 .NET Framework 上运行的异步代码一起使用以防止死锁,阅读 here 了解原因。
我在 ASP.Net Web 表单页面中有以下代码,它主要检查缓存中是否有某些值,如果不在,它会调用一个方法来获取数据,然后将其存储在缓存中。获取数据的方法如下
ChartRenderingHelper.GenerateBidsStatusCreated(currYear.ToString(), currQuarter.ToString(), currYearType.ToString())
使用 EF 调用存储过程,这 3 个调用都调用单独的 SP。 =24=] 这些是并行的,但不确定如何更改我当前的代码来做到这一点。
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 个操作需要 运行 并行所有调用 SQL 使用 EF 的存储过程,这将是一个性能问题。
您可以使用异步来实现此目的,但是最好使用异步实现您的 Entity Framework 添加和获取代码。
首先,我重构了代码以减少重复并通过引入方法 GetOrAddAsync
使其更具可读性,该方法接受缓存键,useCaching
的布尔值(这是什么for?) 和 ChartRenderingHelper
方法的委托。目前还不清楚 currQuarter
和 currYearType
是什么类型。
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
should otherwise be avoided 在任何不是事件处理程序的方法中 - 因为您被限制为具有事件处理程序的 void
return 类型。
正如 LeBigCat 在评论中指出的那样,ConfigureAwait
应该与在 .NET Framework 上运行的异步代码一起使用以防止死锁,阅读 here 了解原因。