-1

Consider this method:

//Called on a known thread
    public async void ThreadSleep()
    {
     while(itemsInQueue)
      {
        //This call is currently on Thread X
        await Task.Delay(5000);
       //This needs to be on the thread that the method was called on
        DoSomeProcessing();
       }
    }

I am assuming that the Task.Delay is executing async on a different thread and resumes on that same thread. This was not very obvious to me. How do I get the method to continue on Thread X?

PS: The ThreadSleep method executes on a non UI thread

Edit: 1) Added W.Brian's code example for simplicity.

2) Yes, this example is exactly that... an example.

3) The purpose of Thread.Delay is just to add some delay between processing.

bobbyalex
  • 2,681
  • 3
  • 30
  • 51
  • 3
    Why do you want to continue on the calling thread? – svick Dec 14 '15 at 16:33
  • Don't know if this is just a contrived example, but you should only call Task.Run for CPU heavy operations. In this case you have a pure async method (`Task.Delay()`), so you should just await it. It's also bad practice to use the method signature `async void` instead of `async Task`, unless you have to because it's an even handler. – sara Dec 14 '15 at 16:51
  • 1
    @svick: Why wouldn't I want it to? Data sync for one. Reentrancy issues. I can think of plenty of reasons why the calls to a method needs to be on a pre determined thread. – bobbyalex Dec 14 '15 at 20:09
  • 4
    All of those things you listed are fixed by correctly returning `async Task` instead of `async void`. [You should never return async void unless you are doing it to be compatible with a event handler](https://msdn.microsoft.com/en-us/magazine/jj991977.aspx). If I am incorrect can you please update your question with a example where you need it to be on the same thread (and that thread is not the UI thread). – Scott Chamberlain Dec 14 '15 at 20:20
  • @ScottChamberlain: How does returning a task fix this problem? – bobbyalex Dec 14 '15 at 20:22
  • 4
    Well, it does not fix this contrived example, it fixes Data sync and reentrancy issues though which is what you told svick why you needed it. If you could post a real example of why you need it I could post an answer explaining how to do it or how a work around will achieve the same goal. I can't do it with your current example because I don't know the "Why" behind `DoSomeProcessing()` must be on the same non UI thread every time, if I know the cause of the requirement I can fulfil the requirement. – Scott Chamberlain Dec 14 '15 at 20:24
  • Whats with the downvotes? The question is perfectly reasonable. I am thankful to Simon who at least helped me look in the right direction. Everyone else seems to be interested in reengineering a system that I have control over. – bobbyalex Dec 15 '15 at 02:55
  • 1
    @bobbyalex I have downvoted your question, because you're asking for something that's generally a bad practice, without giving any justification for why would it make sense in your case. This also makes your question sound like [the XY problem](http://xyproblem.info/). – svick Dec 15 '15 at 13:15

2 Answers2

4

You need to create your own synchronization context (like the UI thread does).

There's a pretty good article on MSDN that helps to understand the problem and how to create a solution.

Mind if I ask why you have to continue on the same thread? Usually it shouldn't create and issues when a new thread is used since the context is preserved.

If you need to preserve some kind of context between calls at a deeper level (like you would do with ThreadLocal), I suggest you use the new AsyncLocal to achieve this goal. It makes sure that immutable objects stay within the async context even if the thread is changed (refer to: How do the semantics of AsyncLocal differ from the logical call context?).

Community
  • 1
  • 1
Simon Mattes
  • 4,866
  • 2
  • 33
  • 53
  • If you check the example you will see that every time the while loop runs, it will execute DoSomeProcessing on a different thread which is not acceptable. – bobbyalex Dec 14 '15 at 20:12
  • @bobbyalex: initially that's correct; but once you create the "SingleThreadSynchronizationContext" and run your delegate within the Run() method, the thread is reserved exclusively for your own dispatcher (example at the very bottom of the article). – Simon Mattes Dec 14 '15 at 22:48
2
await Task.Delay(5000).ConfigureAwait(true);

Calling ConfigureAwait(true) should work as it ensures the same context as the original thread even if the thread changes. This assumes that ThreadLocal<T> is not being used, in which case async/await will generally cause problems and Thread.Sleep may be preferred if you can't change the rest of the code.

Doug Domeny
  • 4,410
  • 2
  • 33
  • 49