简体   繁体   English

使用异步和等待.NET 4.5 MVC C#

[英]Using async & await .net 4.5 mvc c#

I've been experimenting with some old code that needs refactoring in places and was testing if there was any improvement to iis threads etc by uploading file asynchronously (Server Side). 我一直在尝试一些需要重构的旧代码,并通过异步上传文件(服务器端)来测试iis线程等是否有所改进。 Using jQuery file upload client side. 使用jQuery文件上传客户端。

The original code 原始代码

[HttpPost]
public ActionResult UploadDocument( HttpPostedFileBase uploadedFile ) {

  // Do any validation here

  // Read bytes from http input stream into fileData
  Byte[] fileData;

  using ( BinaryReader binaryReader = 
          new BinaryReader( uploadedFile.InputStream ) ) {

    fileData = binaryReader.ReadBytes( uploadedFile.ContentLength );

  }

  // Create a new Postgres bytea File Blob ** NOT Async **
  _fileService.CreateFile( fileData );

  return Json(
    new {
      ReturnStatus = "SUCCESS" // Or whatever
    }
  );

}

The new code 新密码

[HttpPost]
public async Task<ActionResult> UploadDocumentAsync( HttpPostedFileBase uploadedFile ) {

  // Do any validation here

  // Read bytes from http input stream into fileData
  Byte[] fileData = new Byte[uploadedFile.ContentLength];

  await uploadedFile.InputStream.ReadAsync( fileData, 0, uploadedFile.ContentLength );

  // Create a new Postgres bytea File Blob ** NOT Async **
  _fileService.CreateFile( fileData );

  return Json(
    new {
      ReturnStatus = "SUCCESS" // Or whatever
    }
  );

}

The new method appears to work correctly but my question is: 新方法似乎正常工作,但我的问题是:

Is the following code the correct (best) way to do it? 以下代码是正确的(最佳)方法吗? and are there any gotchas doing it this way? 并有这种方法吗? There is a lot of contradictory and out of date information out there. 那里有很多矛盾和过时的信息。 There also seems to be a lot of debate on whether there is any improvement or point in actually doing this. 关于实际执行此操作是否有任何改进或意义,似乎也有很多争论。 Yes it give back threads to iis etc but is it worth the overhead type of debate. 是的,它将线程返回给iis等,但是值得进行开销的辩论。

The code in question 有问题的代码

// Read bytes from http input stream into fileData
Byte[] fileData = new Byte[uploadedFile.ContentLength];

await uploadedFile.InputStream.ReadAsync( fileData, 0, uploadedFile.ContentLength );

The code is correct. 代码正确。 Using async IO is not all or nothing. 使用异步IO并非全部或没有。 You can safely mix sync and async IO, as you have done. 您可以像完成操作一样安全地混合使用同步和异步IO。

Whether you should be using async has been covered already. 是否已经使用异步已经讨论过了。 For ASP.NET the basic rule is to use it in cases where the operation might have a really high latency and is being invoked a lot at the same time. 对于ASP.NET,基本规则是在操作可能具有非常高的延迟并同时被大量调用的情况下使用它。 Only in that case is it important to not free threads. 仅在这种情况下,不释放线程才重要。 If the operations are quick or rare then there are not many threads to free and this is a waste of developer time. 如果操作很快或很少,则没有太多可用线程,这会浪费开发时间。

Reading from a buffered (default) input stream is the same as reading from a file. 从缓冲的(默认)输入流中进行读取与从文件中进行读取相同。 ASP.NET buffers long inputs to files on disk. ASP.NET将长输入缓冲到磁盘上的文件。 This is a classic case where async IO does not provide gains in throughput. 这是典型的情况,其中异步IO无法提高吞吐量。 In fast the file is likely fully cached so that the IO is purely CPU based (a memcpy from the cache). 快速地,文件可能已完全缓存,因此IO完全基于CPU(来自缓存的memcpy)。 Total waste of dev time and CPU cycles. 开发时间和CPU周期的总浪费。 No benefits at all. 完全没有好处。

To answer your questions specifically: 要具体回答您的问题:

  1. There's nothing wrong with your code, and in my opinion you should go forward with that change. 您的代码没有错,我认为您应该继续进行此更改。 Depending on a variety of factors (file sizes/buffering as pointed out, concurrency, etc), it may or may not make a noticeable improvement (and any improvement will be in scalability, not speed), but you will almost certainly not be worse off. 取决于多种因素(指出的文件大小/缓冲,并发等),它可能会或可能不会有明显的改善(任何改善都将在可伸缩性方面,而不是速度方面),但是您几乎肯定不会恶化关。 The "overhead" you speak of involves the state machine written by the compiler, and it is nearly negligable in most situations. 您所说的“开销”涉及编译器编写的状态机,并且在大多数情况下几乎可以忽略不计

  2. One minor gotcha I can think of: You'd want to take a look at what's happening after await and make sure there's no assumptions that that code will be running on the same thread as the code before the await . 我能想到的一个小问题:您想看看await 之后发生了什么并确保没有假设该代码将与await之前的代码在同一线程上运行。 Not likely in this case, but something to be aware of. 在这种情况下不太可能,但是要注意一点。

I would disagree with the notion that async is a "fad" with few good uses on the server. 我不同意异步是服务器上很少使用的“时尚”的观点。 It's a direct response to hardware trends. 这是对硬件趋势的直接响应。 Processor speeds are not increasing at the rate they once did. 处理器速度并未以其曾经的速度提高。 Today the race is for more cores, and we're seeing programming languages and frameworks react to that trend with features that make it easier to write software to take advantage of that hardware-level concurrency. 如今,争夺更多内核的竞争越来越多,我们看到编程语言和框架通过使功能更易于编写软件以利用硬件级并发的功能来响应这一趋势。

Relatively long-running I/O is commonplace in server applications (working with files, calling external APIs, etc), and freeing threads to do other work during those operations is almost always a benefit. 相对长时间运行的I / O在服务器应用程序中很常见(使用文件,调用外部API等),在这些操作期间释放线程以执行其他工作几乎总是有好处的。

For all the trouble you are putting into async await you might be able to do something like this: 对于您在异步等待中遇到的所有麻烦,您也许可以执行以下操作:

try
{
     Task.Run(() => uploadedFile.InputStream.Read( fileData, 0, uploadedFile.ContentLength ));
}
catch()
{
    // etc.
}

This will buy you parallism where you need it (which async does not necessarily give you) and you can spend a little time will a websocket framework like signalR to return your success/failure result. 这将在您需要的地方购买Parallism(异步不一定会给您),并且您可以花一点时间使用诸如signalR的websocket框架来返回成功/失败的结果。

BTW you can probably put a lot more code in that task.. including calls to your db. 顺便说一句,您可能可以在该任务中放置更多代码。包括对数据库的调用。 I just posted that as an example. 我以发布为例。

EDIT ..... Per comments below the point of my answer is that in many cases horsepower can be directed and used with much more prescision with tasks versus async / await. 编辑 .....在我的回答下面,每条评论的意思是,在许多情况下,可以对任务进行定向和使用,而与异步/等待相比,任务更精确。 This is not a slam against async await, just a matter of using the right tool for the job. 这不是针对异步等待的猛烈抨击,而只是为工作使用正确的工具的问题。 Here is some code from a production server that does just exactly that: 以下是生产服务器中的一些代码,它们正是这样做的:

        Task t1 = Task.Run(() => GetBusinessProcesses(-1));
        Task t2 = Task.Run(() => GetActivityRoles(-1));
        Task t3 = Task.Run(() => Tools = toolService.GetTools().ToSelectListItem(x => x.Name, x => x.ToolId.ToString(), true).ToList());
        Task t4 = Task.Run(() => BusinessAreas = businessAreaService.GetBusinessAreas().ToSelectListItem(x => x.Name, x => x.BusinessAreaId.ToString(), true).ToList())
            .ContinueWith(t =>
        {
            // Populate the functional area dropdown data
            if (BusinessAreas != null && BusinessAreas.Any())
            {
                int businessAreaId = System.Convert.ToInt32(BusinessAreas[0].Value);
                GetFunctionalAreas(businessAreaId);
            }
        });

        try
        {
            Task.WaitAll(t1, t2, t3, t4);
        }
        catch (Exception ex)
        {
            throw new Exception("Error populating dropdowns.  See inner exception.", ex);
        }

In the code above I make four parallel calls to my db and build four lists in parallel. 在上面的代码中,我对数据库进行了四个并行调用,并并行构建了四个列表。 I wait on the tasks and return the page. 我等待任务并返回页面。 In the OP's case he can do: 对于OP,他可以执行以下操作:

    Task t1 = Task.Run(() => parse the file).ContinueWith(x => write to the db);
    Task t2 = Task.Run(() => more work);
    // do some work here
    Task.WaitAll(t1, t2);
    // Return the page 

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM