DEV Community

Discussion on: How C# asynchronous programming is different than what you think

Collapse
 
yaser profile image
Yaser Al-Najjar • Edited

I agree with you, async-ness has been there for long in different flavors.

And as you said regarding Twisted, EDA (event driven architecture) is never pleasant, and it's hard to predict and even harder to test!

I also really believe that simpler techs like Node which gives the developers easier way to achieve good-enough products (for tens of thousands of users) made the adoption easier rather than all-async-hard-to-understand techs.

async is implemented on top of a thread pools

async/await uses no threads, you can imagine it as a call-back. (this is a more detailed explanation)

the fact that it is implemented transparently on top of threads means that it can potentially use multiple cores (though it's not guaranteed)

Since no threading is used with async/await, it's mainly meant for IO-bound operations (threads still have their place in CPU intensive operations.)


Fun story:

We built once a simple registration sub-system that sends a verification email to users once they register, very simple and straight forward with SMTP.
And that time we used the magic of C# async/await, it was good... but in some rare cases, the mails aren't sent simply cuz that's how SMTP works (it might drop the connection time to time with more requests coming in).

We decided to switch into Django/Python cuz it has tons of 3rd part libs that serve our goals really well. So it means we will lose the magic of C# async/await!

Of course more mails were dropping after the switch, until we thought about using Amazon SQS to keep the request of sending the mail in the queues and never allow it to pass from the queue till it's successfully sent... guess what happened? we got a heck more reliable and faster mail sending, and I've never heard any user complaining about his verification email after!

Collapse
 
rhymes profile image
rhymes • Edited

async/await uses no threads, you can imagine it as a call-back. (this is a more detailed explanation)

are you sure? I'd agree normally but in this article I found on MSDN: Task-based asynchronous pattern (TAP) and Async in depth linked from Async overview they clearly talk about async tasks mapped on a thread pool.

@vekzdran also linked article ASP.NET Core SynchronizationContext which talks about thread pooling again.

But probably we're talking about two slightly different things due to my lack of familiarity with .NET and C#.

async/await by themselves don't introduce threads, but ASP.NET uses async on top of a pool to handle concurrent requests? Is that the source of my confusion?

Since no threading is used with async/await, it's mainly meant for IO-bound operations (threads still have their place in CPU intensive operations.)

I read this in the article you linked:

The async and await keywords don't cause additional threads to be created. Async methods don't require multithreading because an async method doesn't run on its own thread. The method runs on the current synchronization context and uses time on the thread only when the method is active. You can use Task.Run to move CPU-bound work to a background thread, but a background thread doesn't help with a process that's just waiting for results to become available.

I'm a little confused by the paragraph you linked. What this paragraph says to me is that calling await something does not generate a thread (which makes sense, otherwise every call would create its own thread and add a lot of context switching). But it doesn't exclude the fact that an async operation MIGHT be running on a different thread than the one it was called from (which is compatible with the idea of pooling, see what happens in Go with goroutines which are transparently mapped on a pool of threads). As it says, if you have a CPU intensive operation (perhaps blocking the context switch of the continuations) you can explicitly tell the compiler to move the operation to a background thread. So yeah, from what I've gathered by this and the other articles thread pooling MIGHT be involved or not, it's just not visible to the programmer (which is why it's a great implementation).

we got a heck more reliable and faster mail sending, and I've never heard any user complaining about his verification email after!

Yeah, putting a queue between you and the SMTP server was a good idea :)
You might want to follow Christine Spang if you're still interested in all things "Python + email":

spang image
Thread Thread
 
yaser profile image
Yaser Al-Najjar

My rule of thumb is:

  • If I want nonblocking code that is related to IO (say talk to db) without creating a thread, I simply throw async/await everywhere.

  • If I want to create a thread to do some cpu heavy computation, I add Task.Run and fill in the lambda my sync code (and for sure that requires adding async/await).

This is the way I do async C#... I should say I never tried to check those statements 😁

Also, some operations might lead the OS to create a thread, but that doesn't count on our app process (it's the way the OS does its business instead).

Thanks for the recommendation, I just read her posts and they seem interesting!

Collapse
 
vekzdran profile image
Vedran Mandić

This is a cool example on how async/await really helps (you had to go into a message queue service instead of wrangling async await, woah, hope you solved that somehow). You did fix it but introducing so much infrastructure, is that right? I mean, you solved the issue that's what counts, but is it worth? Thanks.

Thread Thread
 
yaser profile image
Yaser Al-Najjar • Edited

That infrastructure is really cheap, we thought about adding redis or rappit MQ, but we made our minds with SQS cuz it's 10x cheaper (cuz we didn't wanna change our server plan to add more memory, and not mentioning ensuring the MQ runs with no problems).

For the technical cost, it took us about 3 days to add celery in front of the mail sender and deploy it on production (after deploying and trying it on staging).

I know took a bit of time and effort to get things done, even we had already the resend verification button... but we kept getting moaning from our beloved users 😄

So, for stopping the users' moaning, I would say it's totally worth it!