如何在相关集合之间添加异步切换C#

问题描述:

我有项目需要通过id从数据库中收集文件,将这些文件压缩起来,将压缩文件上传到第三方Web服务,并对响应进行一些操作。我正在使用Parallel.ForEach来尝试完成此操作,但我无法使其异步运行,而且我甚至不确定这是实现此目的的最佳方式。有没有办法异步运行,并更有效?如何在相关集合之间添加异步切换C#

public async Task<string> UploadZip(string courseID) 
    { 
     string result = string.empty; 
     ids = courseID.Split(',').ToList(); 

     ConcurrentStack<string> zipPaths = new ConcurrentStack<string>(); 

     Parallel.ForEach(ids, (id) => 
     { 
      files = rep.GetFiles(id); 
      ZipFiles zf = new ZipFile(files);  
      zipPaths.Push(zf.ZipFilePath); 
      try 
      { 
       while (zipPaths.Count > 0) 
       { 
        FileHolder fh; 
        if (fileHolder.TryPop(out fh)) 
        { 

          FileInfo file = new FileInfo(fh.EndPoint); 

          if (file != null && file.Length > 0) 
          { 
           // string fileName = Path.GetFileName(path); 
           result = CallService(zf.PathToZip); 
           DoSomethingElse oSomethingElse = new DoSomethingElse(result, id, version); 
          }        

        } 
       } 
      } 
      catch (Exception ex) 
      { 
       System.Diagnostics.Debug.WriteLine(string.Concat("HomeController line 123","error ",ex.Message, DateTime.Now.TimeOfDay)); 
      } 
      ITermExtractorContent result = new TermExtractorContent(); 

     }); 


     return output; 
    } 

    private static async Task<string> CallService(string pathToZip) 
     { 

      using (var client = new HttpClient()) 
      { 
       //headers for client 
       try 
       { 
        using (var content = new MultipartFormDataContent()) 
        { 
         using (var stream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) 
         { 
          using (var fileContent = new StreamContent(stream)) 
          { 
           fileContent.Headers.Add("Content-Disposition", "form-data; name=\"file\"; filename=\"" + Path.GetFileName(path) + "\""); 
           content.Add(fileContent, "file", Path.GetFileName(pathToZip)); 
           using (
            var message = 
             await client.PostAsync(baseEndpoint, content)) 
           { 
            output = await message.Content.ReadAsStringAsync(); 

           } 
          } 
         } 
        }//end using 

       } 
       catch (Exception ex) 
       { 
        log(ex); 
       } 

      }// using (var client = new HttpClient()) 
      return output; 
     } 
+0

没有理由使用Async进行CPU绑定操作。异步基本上是一种防止线程在等待外部源时无所事事的方式。你的代码做了很多工作,完全是一个线程。 –

+0

我看到你的第一个方法没有任何await关键字;这意味着你的代码并不是异步的。你必须得到一个编译器警告,说明一样。 –

+0

@ErikPhilips但我想有两个进程1.one用于获取文件和压缩它们2.另一个用于压缩文件,上传,等待响应。除非我错了,否则应比2少花时间 - 因此基本上需要两个线程。 – user2049142

我看到这需要异步的唯一原因是为了防止阻塞UI线程。我不确定DoSomethingElse()会做什么(这是您尝试同时运行的方法吗?)以及如何使用结果变量作为参数,但我觉得上面的行result = CallService(zf.PathToZip);应该是result = await CallService(zf.PathToZip);由于当前函数被标记为异步它会通知编译器创建一个回调,以便在任务返回时执行,并在那里恢复执行。当你的程序碰到该方法时,它将不会继续执行,直到它被返回。其他线程(如称为异步方法的线程)将继续执行。给你打电话UploadZip()方法将它使用类似:因为这

public void DoThings() 
{ 
    UploadZip("MyString"); 
    DoSomethingElseConcurrently(); 
} 

也请记住线程安全(竞争条件,死锁等)是异步编程。在这里使用TPL时会遇到一些小问题,因为您可能会发现自己嵌套了异步方法调用(就像您在此处所做的那样)。外部异步调用不能使用.Result()方法,因为它阻止该线程。内部异步线程将尝试返回到调用它的方法,从而进入死锁。

+0

我在UI中使用了一个进度条,所以我肯定不想阻止它。 – user2049142