I feel that c# has become a very wordy language and I'm not happy to have to code in the async style like this.
Oh, but that is not wordy at all. You are not writting something like this:
client.GetAsync("http://www.nzherald.co.nz/").Then
(
response => response.Content.ReadAsStringAsync().Then
(
pageContents =>
{
Console.WriteLine(pageContents);
Console.ReadLine();
}
)
);
And an API like the one above (probably a wrapper around ContinueWith
) it won't handle exceptions properly, you would probably need an Error
method with a callback to get the exceptions.
No, C# is not wordy.
Addendum: perhaps it is worth mentioning that if an async
method returns a Task
, it does not means you have to await
it. If you want the Task
to do something else with it (for example place it an array for Task.WaitAll
or Task.WhenAll
), you do not put the await
keyword. Therefore, the await
keyword is not redundant.
is creation of threads for async code going to impact performance?
No. Although some API do actually need to block on a Thread
– and I think that is the case for listening on a socket, at least on Windows – it will use the ThreadPool
, thus there won't be a lot of new Thread
s created.
That it will use the ThreadPool
is not true for every API. Most will follow the following pattern:
The library creates a TaskCompletionSource
.
The library sets a means to receive a notification. Callback, timer, message, whatever...
The library sets code to react to the notification that will call SetResult
, or SetException
on the TaskCompletionSource
as appropriate for the notification received.
The library does the actual call to the external system.
The library returns TaskCompletionSource.Task
.
The call from step 4 goes to the external system. The external system responds, the response is received on the notification mechanism from step 2, then it sets the Task
completed, and afterwards the continuation of the Task
is scheduled (that continuation is what happens after your await
).
How can I throttle the rate of calls if I'm calling GetAsync in a loop?
If you are awaiting it, then your code will not continue until it completes. Alternatively, if we are talking about having a limited number of Task
s concurrently (not being awaited), I would suggest to use a SemaphoreSlim
. I would use it in conjunction with Task.WaitAny
or Task.WhenAny
.
Besides that, you probably will find await Task.Delay(milliseconds)
useful. It will use the pattern I described above, around a timer, no Thread
will be blocked waiting (unlike Thread.Sleep
).
has the c# team gone in the right direction creating this async paradigm in the language?
I believe async/await
is generally good. I agree that there is some overhead (creating Task
objects, et. al.). However, C# is doing better than most languages.
I understand the blue-red argument...
... on that, I would argue that if you want to follow the idea of a pure core and an impure shell, you will find that pure and impure and async
and "sync" are similar. The impure shell would deal with external systems. If you call an impure method from your method, your method is impure. Similarly, interacting with external systems is often async
, and if you want to await
an async
method, you have to make your method async
.
So, yeah, async
propagating is annoying. The solutions is that the entry point will be impure (and async
) it will deal with external systems (impure imperative shell) and call into your not async
code (pure functional core), which returns to the shell for more interoperability. That way you do not have to make everything async
, and you do not have to make your whole code impure. If you are isolating external systems (for ease of exchanging them and ease of testing), you are probably doing this already.
I feel that go's approach of creating a thread by just saying go myfunction() is less long-winded, but I know it has it's performance penalties due to the green-threads used.
Is your complain that await
is longer to type than go
? that explains "wordy".
Asynchronous does not mean multi-threaded. For instance, we can read for the hard disk without having a Thread
waiting. You initialize the buffer where you will read, tell the operating system to tell the driver to tell the hard disk to write to that buffer (via DMA) and trigger an event. Then in the event we do TaskCompletionSource.SetResult
and then system can schedule the continuation (your code after await
). No Thread
, and you didn't have to worry about it. And yes, that is the same pattern I described above.
I find it hard to reason about what is happening behind the scenes of the compiler
The compiler is rewriting your code as continuations. It is a state machine. This is not the first time the compiler rewrites your code as a state machine, that would have been co-routines iterators with yield return
.
For an async
method, each await
means that the code afterwards will be a continuation the Task
being awaited.
Please note that the Thread
calling will not be waiting. Instead, the async method schedules the first Task
, sets its continuation, and returns a Task
that will completed when all the continuations created for the method completes.
Actually, it is a bit more complicated because there is error handling, and it works with try-catch-finally
. The compiler does the rewriting.
That probably means that the calling Thread
will eventually be free to do work, if that is the case it could be used to run scheduled Task
s. Sometimes the continuation will run on the same Thread
that called it, sometimes it will run on a different one. It is up to the TaskScheduler
. The default TaskScheduler
will use the ThreadPool
. And, yes, you can write a custom TaskScheduler
.
Perhaps the interview Mads Torgersen: Inside C# Async can help you understand further. Strongly recommended.
ConfigureAwait
totrue
. But since you complaining, how would you design it better -- face a goal: your solution has to be back-compatible, it should provide asynchronous calls (because performance matters).