So, in C#, I understand the historical difference between the two vaguely; a Task is a newer concept and the highest level concurrency native C# offers.
AsyncResult is a bit more ambiguous. For example, the docs say BeginRead relies on the underlying stream for non blocking behavior.
I know ReadAsync creates a Task which is handled by a Scheduler on the ThreadPool and therefore is guaranteed to not block, but then I read about C# delegates and Begin/EndInvoke which allow "any synchronous method to be asynchronous"
Is this true? As the documentation around BeginRead seems to indicate it still blocks if the underlying stream blocks. Is this equivalent to creating a Task, just less developer friendly?
Is a Task always preferred to an AsyncResult? Why or why not?
I surely hope this is the correct place to ask this.
1 Answer 1
I know ReadAsync creates a Task which is handled by a Scheduler on the ThreadPool and therefore is guaranteed to not block, but then I read about C# delegates and Begin/EndInvoke which allow "any synchronous method to be asynchronous"
Is this true?
Yes, ReadAsync
will not block, and yes, it will use the thread pool.
And yes delegate.BeginInvoke
and delegate.EndInvoke
will not block either. And yes, they use the thread pool. And you should avoid them. Please have a look at Difference between delegate.BeginInvoke and using ThreadPool threads in C#.
As the documentation around BeginRead seems to indicate it still blocks if the underlying stream blocks. Is this equivalent to creating a Task, just less developer friendly?
Stream.BeginRead
is virtual. The class that derives from Stream
can mess with it. So it might block on some streams. It should not be a problem for IO stream. However, the reference source for Stream
also suggest that some platform could use blocking streams if they do not support async IO.
Is a Task always preferred to an AsyncResult? Why or why not?
Yes.
AsyncResult
is a promise, kind of like how Task
is a promise. Except, it is worse.
An AsyncResult
could be completed synchronously, or asynchronously. Kind of like Task
. But if the AsyncResult
did not complete synchronously, you get a WaitHandle
(AsyncResult.AsyncWaitHandle
). And that means you will either have to pool or block on a thread to to wait on it...
Or do you? You can use ThreadPool.RegisterWaitForSingleObject
to wait on the WaitHandle
from the AsyncResult
.
Add to that that AsyncResult
does not express cancellation nor timeout by itself. You can specify your timeout to wait on the WaitHandle
... but for cancellation you might be forced to create another WaitHandle
for the cancellation then use WaitHandle.WaitAny
... and of course handle the end method appropriately.
Finally, AsyncResult
is the promise producer facing API, IAsyncResult
is the promise consumer facing API. Because of this, you probably don't want to expose any AsyncResult
in your API, only expose IAsyncResult
if anything.
I would advice against using AsyncResult
at all. If you use AsyncResult
, messing with the producer is just a cast appart.
Of course, Task
is better.
-
I was mistaken, I meant to say IAsyncResult the entire time, but this explanation makes sense. I was specifically wondering about the differences between Stream.BeginRead and Stream.ReadAsync. is it safe to say one handles a higher concurrent load than the other? i.e. if I want to make an application that makes thousands of arbitrary reads concurrently, would one perform better or is the difference mostly just in code?gabriel.hayes– gabriel.hayes2020年02月08日 17:42:01 +00:00Commented Feb 8, 2020 at 17:42
-
@user1538301 hmm... I would argue that
BeginRead
is lower level, you probably can built something for such use case which is more performant by avoiding some unnecessary overhead (I would have to actually try and benchmark). It is, of course, harder to use, and thus it will be easier to introduce bugs usingBeginRead
. Which means that, as developer, you will be less productive. Now, it is economics.Theraot– Theraot2020年02月08日 18:05:23 +00:00Commented Feb 8, 2020 at 18:05 -
@user1538301 thinking about it, using
BeginRead
andEndRead
wil likely result in more unmanaged resources (WaitHandle
), whileTask
would avoid those.Theraot– Theraot2020年02月08日 19:45:52 +00:00Commented Feb 8, 2020 at 19:45 -
I shall benchmark it and find out. Thanksgabriel.hayes– gabriel.hayes2020年02月08日 22:57:39 +00:00Commented Feb 8, 2020 at 22:57