我是否为Task.WhenAll()创建了死锁

时间:2015-02-20 12:08:07

标签: c# .net task-parallel-library async-await deadlock

我似乎遇到了以下代码的死锁,但我不明白为什么 从代码中的某一点开始我称之为这种方法。

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(...)没有完成?我不明白正确使用等待吗? 如果我同步运行任务,我会得到结果,但GUI会冻结:

foreach (var task in tasks)
    task.RunSynchronously();

我阅读了各种解决方案,但大多数都与Task.WaitAll()结合使用,因此没有多大帮助。我也尝试使用DisplaySearchResult中的帮助,正如您在MethodThatCallsWebservice中看到的那样,但我无法使其发挥作用。

更新1: 方法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 }

{{1}}

2 个答案:

答案 0 :(得分:3)

由于您拥有GetStuffGetStuffAsync)的异步版本,因此使用它而不是将同步GetStuff卸载到ThreadPool线程要好得多与Task.Run。这会浪费线程并限制可伸缩性。

async方法返回&#34; hot&#34;任务,因此您不需要致电Start

IEnumerable<Task<FolderResult>> MethodThatCallsWebservice(ServiceClient serviceClient, SearchData searchData)
{
    return keys.Select(async key => 
        new FolderResult(key, await serviceClient.GetStuffAsync(input)));
}

答案 1 :(得分:1)

您需要在返回任务之前开始执行任务。或者甚至更好地使用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)));