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). Using jQuery file upload client side.
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.
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 );
-
2async /await is the tail wagging the dog I think. Would love to see some arguments that prove me wrong. This is a good question. Therefore, it will almost certainly be deleted by some crossing guard admin.user3230660– user32306602015年04月03日 14:32:28 +00:00Commented Apr 3, 2015 at 14:32
-
Why did you not make CreateFile async as well? This is a test question to see whether you have a particular common misunderstanding or not. Meanwhile I'd like to refer to you to my treatment of this topic: stackoverflow.com/a/25087273/122718 and stackoverflow.com/a/12796711/122718.usr– usr2015年04月03日 14:52:21 +00:00Commented Apr 3, 2015 at 14:52
-
usr I probably will but thats not really what I was asking. Though on that topic there is much debate on whether fast local database access is quicker left synchronous.William Humphreys– William Humphreys2015年04月03日 14:56:19 +00:00Commented Apr 3, 2015 at 14:56
2 Answers 2
The code is correct. Using async IO is not all or nothing. You can safely mix sync and async IO, as you have done.
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. 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. This is a classic case where async IO does not provide gains in throughput. In fast the file is likely fully cached so that the IO is purely CPU based (a memcpy from the cache). Total waste of dev time and CPU cycles. No benefits at all.
11 Comments
To answer your questions specifically:
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.
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 theawait
. 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.
Comments
Explore related questions
See similar questions with these tags.