1

I'm working on a c# application, it has a background thread that takes care of a lot of tasks e.g. call HTTP/REST endpoint, do basic calculation, send data over socket etc. All the operations are queued to the background thread, it dequeues them individually, processes them and then on to the next one.

I've been thinking about using async in the background thread e.g. call the HTTP endpoint using async/await semantics but not sure how that would affect the background thread. Lets says background thread calls an HTTP endpoint using async, how do I structure the code so that it dequeues next packet and processes that?

tunafish24
  • 2,288
  • 6
  • 28
  • 47
  • Have you looked at this: https://stackoverflow.com/questions/20261300/what-is-correct-way-to-combine-long-running-tasks-with-async-await-pattern. It tackles this issue about combining the two techniques in a correct manner. – Iliass Nassibane May 07 '20 at 20:29
  • Stop using background workers - they are old school now. Try Tasks instead - they specifically work with `async`/`await`. – Enigmativity May 08 '20 at 02:58
  • Do you want to allow multiple asynchronous operations to run concurrently? Or you prefer that each operation will start after the completion of the previous one? – Theodor Zoulias May 08 '20 at 07:27

2 Answers2

2

I've been thinking about using async in the background thread e.g. call the HTTP endpoint using async/await semantics but not sure how that would affect the background thread. Lets says background thread calls an HTTP endpoint using async, how do I structure the code so that it dequeues next packet and processes that?

await has "hooks" that you can use to control the default resuming behavior. await on its own (if used all the way) will "yield" back to your message processing loop; that's no problem. But by default, when the await resumes executing its method, that method would run on a thread pool thread, not on your dedicated background thread.

If you want code after await to resume on your background thread, you'll need to create a SynchronizationContext that queues work to your background thread's queue, and ensure that it is set as the current SynchronizationContext for any code that runs on your background thread. I wrote an AsyncContextThread that is a background thread with message queue and SynchronizationContext; that should be a good starting point.

Stephen Cleary
  • 437,863
  • 77
  • 675
  • 810
  • And why would he want to resume on the same thread with the continuation? Any particular reason? – Piotr May 08 '20 at 09:43
  • @Piotr: It's usually the intended behavior. If a dev queues an async method to a background thread runner, they usually find it surprising that the method jumps to a thread pool thread after its `await`. – Stephen Cleary May 08 '20 at 11:44
  • Exactly, so no apparent reason to resume on the same (background) thread i suppose. – Piotr May 08 '20 at 12:12
-1

You can mixed both async/await and running on a background thread. Async/Await will not "affect" background thread anyhow. But please do remember, that underneath the async/await there are Task involved (and state machine).

Fun fact is - that when you decompile async/await code --> there is no async/await there;) You could say it is a syntax suger.

More info on how it is organised - for example here: https://ranjeet.dev/understanding-how-async-state-machine-works/

So it could happen, that when you return from the async operation (for example you will start receiving response from HTTP request) - it is possible you will end up on a different thread and the rest of the code will be processed on a different thread. Normally it is not an issue, but sometimes it does make a difference (for example on ASP.NET - httpcontext can be lost because of that)

You did ask also "how do i structure code" And that is the beauty of async/await. You don't :) All you have to change is to change everything to async/await - because the rule : "Async all the way" is very important" More on the important rules with asynchronous programing here: https://learn.microsoft.com/en-us/archive/msdn-magazine/2013/march/async-await-best-practices-in-asynchronous-programming

So for example if you did have code like this:

public string SendHttpRequest()
{
    using (var client = new WebClient())
    {
        return client.DownloadString(new Uri("http://www.example.com/example.aspx"));
    }
}

you will just have to change it to:

public async Task<string> SendHttpRequestAsync()
{
    using (var client = new WebClient())
    {
        return await client.DownloadStringTaskAsync("http://www.example.com/example.aspx");
    }
}

And then of course each place in the code - you have to change to async (and you have to call this method and all method that will become async with await) That is the rule of "async all the way" Don't go into temptation to use it like this somewhere in the code:

SendHttpRequestAsync().Result --> beacause it saves you from adding async on the method;)

Then you miss the point of using asyncs and realy bed things can happen (try to do something like this in Winforms with some OnClick event :) )

Piotr
  • 1,155
  • 12
  • 29