我是否为 Task.WhenAll() 创建死锁
Do I create a deadlock for Task.WhenAll()
我似乎遇到了以下代码的死锁,但我不明白为什么。
从代码的某个点开始,我调用了这个方法。
public async Task<SearchResult> Search(SearchData searchData)
{
var tasks = new List<Task<FolderResult>>();
using (var serviceClient = new Service.ServiceClient())
{
foreach (var result in MethodThatCallsWebservice(serviceClient, config, searchData))
tasks.Add(result);
return await GetResult(tasks);
}
其中GetResult如下:
private static async Task<SearchResult> GetResult(IEnumerable<Task<FolderResult>> tasks)
{
var result = new SearchResult();
await Task.WhenAll(tasks).ConfigureAwait(false);
foreach (var taskResult in tasks.Select(p => p.MyResult))
{
foreach (var folder in taskResult.Result)
{
// Do stuff to fill result
}
}
return result;
}
var result = new SearchResult();
行永远不会完成,尽管 GUI 由于以下代码而响应:
public async void DisplaySearchResult(Task<SearchResult> searchResult)
{
var result = await searchResult;
FillResultView(result);
}
此方法是通过调用 Search 方法的事件处理程序调用的。
_view.Search += (sender, args) => _view.DisplaySearchResult(_model.Search(args.Value));
DisplaySearchResult
的第一行被调用,它沿着路径向下到达具有 Task.WhenAll(...)
部分的 GetResult 方法。
为什么 Task.WhenAll(...)
从未完成?是我没有正确理解 await 的用法吗?
如果我 运行 同步任务,我确实得到了结果,但随后 GUI 冻结了:
foreach (var task in tasks)
task.RunSynchronously();
我阅读了各种解决方案,但大多数都与 Task.WaitAll()
结合使用,因此没有多大帮助。正如您在 DisplaySearchResult
中看到的那样,我也尝试使用 this blogpost 的帮助,但我没能成功。
更新一:
方法 MethodThatCallsWebservice
:
private IEnumerable<Task<FolderResult>> MethodThatCallsWebservice(ServiceClient serviceClient, SearchData searchData)
{
// Doing stuff here to determine keys
foreach(var key in keys)
yield return new Task<FolderResult>(() => new FolderResult(key, serviceClient.GetStuff(input))); // NOTE: This is not the async variant
}
您需要在 return 之前开始您的任务。或者更好地使用 Task.Run。
这个:
yield return new Task<FolderResult>(() =>
new FolderResult(key, serviceClient.GetStuff(input)))
// NOTE: This is not the async variant
最好写成:
yield return Task.Run<FolderResult>(() =>
new FolderResult(key, serviceClient.GetStuff(input)));
由于您有 GetStuff
(GetStuffAsync
) 的异步版本,因此最好使用它而不是将同步 GetStuff
卸载到 ThreadPool
线程 Task.Run
。这会浪费线程并限制可伸缩性。
async
方法 return 一个 "hot" 任务,所以你不需要调用 Start
:
IEnumerable<Task<FolderResult>> MethodThatCallsWebservice(ServiceClient serviceClient, SearchData searchData)
{
return keys.Select(async key =>
new FolderResult(key, await serviceClient.GetStuffAsync(input)));
}
我似乎遇到了以下代码的死锁,但我不明白为什么。
从代码的某个点开始,我调用了这个方法。
public async Task<SearchResult> Search(SearchData searchData)
{
var tasks = new List<Task<FolderResult>>();
using (var serviceClient = new Service.ServiceClient())
{
foreach (var result in MethodThatCallsWebservice(serviceClient, config, searchData))
tasks.Add(result);
return await GetResult(tasks);
}
其中GetResult如下:
private static async Task<SearchResult> GetResult(IEnumerable<Task<FolderResult>> tasks)
{
var result = new SearchResult();
await Task.WhenAll(tasks).ConfigureAwait(false);
foreach (var taskResult in tasks.Select(p => p.MyResult))
{
foreach (var folder in taskResult.Result)
{
// Do stuff to fill result
}
}
return result;
}
var result = new SearchResult();
行永远不会完成,尽管 GUI 由于以下代码而响应:
public async void DisplaySearchResult(Task<SearchResult> searchResult)
{
var result = await searchResult;
FillResultView(result);
}
此方法是通过调用 Search 方法的事件处理程序调用的。
_view.Search += (sender, args) => _view.DisplaySearchResult(_model.Search(args.Value));
DisplaySearchResult
的第一行被调用,它沿着路径向下到达具有 Task.WhenAll(...)
部分的 GetResult 方法。
为什么 Task.WhenAll(...)
从未完成?是我没有正确理解 await 的用法吗?
如果我 运行 同步任务,我确实得到了结果,但随后 GUI 冻结了:
foreach (var task in tasks)
task.RunSynchronously();
我阅读了各种解决方案,但大多数都与 Task.WaitAll()
结合使用,因此没有多大帮助。正如您在 DisplaySearchResult
中看到的那样,我也尝试使用 this blogpost 的帮助,但我没能成功。
更新一:
方法 MethodThatCallsWebservice
:
private IEnumerable<Task<FolderResult>> MethodThatCallsWebservice(ServiceClient serviceClient, SearchData searchData)
{
// Doing stuff here to determine keys
foreach(var key in keys)
yield return new Task<FolderResult>(() => new FolderResult(key, serviceClient.GetStuff(input))); // NOTE: This is not the async variant
}
您需要在 return 之前开始您的任务。或者更好地使用 Task.Run。
这个:
yield return new Task<FolderResult>(() =>
new FolderResult(key, serviceClient.GetStuff(input)))
// NOTE: This is not the async variant
最好写成:
yield return Task.Run<FolderResult>(() =>
new FolderResult(key, serviceClient.GetStuff(input)));
由于您有 GetStuff
(GetStuffAsync
) 的异步版本,因此最好使用它而不是将同步 GetStuff
卸载到 ThreadPool
线程 Task.Run
。这会浪费线程并限制可伸缩性。
async
方法 return 一个 "hot" 任务,所以你不需要调用 Start
:
IEnumerable<Task<FolderResult>> MethodThatCallsWebservice(ServiceClient serviceClient, SearchData searchData)
{
return keys.Select(async key =>
new FolderResult(key, await serviceClient.GetStuffAsync(input)));
}