2015-11-17 33 views
3

我对我的代码的行为有些困惑[下面]。我正在开发一个专门的命令行工具,用于下载和处理一些文件。我尝试在可能的情况下使用c#的异步功能。当创建任务并使用Task.WaitAll()时,代码片段按预期运行。等待之后,我有两个任务都被标记为完成。问题:我试图从任务中获取结果最终会再次运行两个任务!为什么是这样?如何在不执行第二次任务的情况下读取结果?完成的c#任务再次运行...为什么?

private IEnumerable<Task<FileInfo>> DownloadFiles() 
    { 
     int fileCount = 1; 

     Console.Clear(); 
     Console.SetCursorPosition(0, 0); 
     Console.Write("Download files..."); 

     yield return DownloadFile(Options.SkuLookupUrl, "SkuLookup.txt.gz", fileCount++, f => 
     { 
      return DecompressFile(f); 
     }); 
     yield return DownloadFile(Options.ProductLookupUrl, "ProductList.txt.gz", fileCount++, f => 
     { 
      return DecompressFile(f); 
     }); 
    } 

    public void Execute() 
    { 
     var tasks = DownloadFiles(); 
     Task.WaitAll(tasks.ToArray()); 

     Console.WriteLine(); 
     Console.WriteLine("Download(s) completed. Parsing sku lookup file."); 
     FileInfo[] files = tasks.Select(t => t.Result).ToArray(); // <-- triggers a second round of task execution 

     ParseSkuLookups(files.SingleOrDefault(f => f.Name.ToLowerInvariant().Contains("skulookup"))); 
    } 

如果相关这里是下载方法:

private async Task<FileInfo> DownloadFile(string targetUrl, string destinationFile, int lineNumber, Func<FileInfo,FileInfo> callback = null) 
    { 
     FileInfo fi = new FileInfo(destinationFile); 

     if (!Options.NoCleanup || !fi.Exists) 
     { 
      WebClient client = new WebClient(); 
      client.DownloadProgressChanged += (s, e) => 
      { 
       char spinnerChar; 

       switch ((e.ProgressPercentage % 10)) 
       { 
        case 0: spinnerChar = '|'; break; 
        case 1: spinnerChar = '/'; break; 
        case 2: spinnerChar = '-'; break; 
        case 3: spinnerChar = '|'; break; 
        case 4: spinnerChar = '\\'; break; 
        case 5: spinnerChar = '|'; break; 
        case 6: spinnerChar = '/'; break; 
        case 7: spinnerChar = '-'; break; 
        case 8: spinnerChar = '\\'; break; 
        default: 
        case 9: spinnerChar = '|'; break; 

       } 
       lock (ConsoleLockSync) 
       { 
        Console.SetCursorPosition(0, lineNumber); 
        Console.WriteLine(String.Format("{0} download: {1}% {2}", 
         destinationFile, e.ProgressPercentage==100 ? "[Complete]" : spinnerChar.ToString())); 
       } 
      }; 
      await client.DownloadFileTaskAsync(new Uri(targetUrl, UriKind.Absolute), destinationFile); 
     } 
     else if(Options.NoCleanup) 
     { 
      lock (ConsoleLockSync) 
      { 
       Console.SetCursorPosition(0, lineNumber); 
       Console.WriteLine(String.Format("{0} download: Skipped [No Cleanup]  ", destinationFile)); 
      } 
     } 
     fi.Refresh(); 
     return callback != null ? callback(fi) : fi; 
    } 
+1

哦,顺便说一句。调用'.WaitAll('在异步方法上很容易让你死锁,你应该让Execute返回一个'async Task'并且执行一个'await Task.WhenAll(tasks)'或者用来使用'.ConfigureAwait(false) ''DownloadFile'内每等待电话# –

+0

两个答案的价格一...这是一个控制台应用程序,但是,所以我不过分关注死锁,但这将是有用的信息在其他地方,我再次感谢! –

回答

7

当你有你每次枚举的结果将重新运行功能的时间yield return implmented一个IEnumerabletasks.ToArray()Task.WaitAll(tasks.ToArray());列举一次,然后您再枚举tasks.Select(t => t.Result).ToArray();。为了让它枚举一次保留第一个ToArray()调用的结果,然后在整个方法中重复使用该结果。

public void Execute() 
{ 
    var tasks = DownloadFiles(); 
    var taskArray = tasks.ToArray(); 
    Task.WaitAll(taskArray); 

    Console.WriteLine(); 
    Console.WriteLine("Download(s) completed. Parsing sku lookup file."); 
    FileInfo[] files = taskArray.Select(t => t.Result).ToArray(); // <-- notice we use taskArray here instead of tasks. 

    ParseSkuLookups(files.SingleOrDefault(f => f.Name.ToLowerInvariant().Contains("skulookup"))); 
} 
+0

哇......我不知道屈服回报的结果会是这样的(只是为了熟悉这个功能)。谢谢修复! –

相关问题