I'm getting confusing behavior when using a different SynchronizationContext inside an async function than outside.
Most of my program's code uses a custom SynchronizationContext that simply queues up the SendOrPostCallbacks and calls them at a specific known point in my main thread. I set this custom SynchronizationContext at the beginning of time and everything works fine when I only use this one.
The problem I'm running into is that I have functions that I want their await continuations to run in the thread pool.
void BeginningOfTime() {
// MyCustomContext queues each endOrPostCallback and runs them all at a known point in the main thread.
SynchronizationContext.SetSynchronizationContext( new MyCustomContext() );
// ... later on in the code, wait on something, and it should continue inside
// the main thread where MyCustomContext runs everything that it has queued
int x = await SomeOtherFunction();
WeShouldBeInTheMainThreadNow(); // ********* this should run in the main thread
}
async int SomeOtherFunction() {
// Set a null SynchronizationContext because this function wants its continuations
// to run in the thread pool.
SynchronizationContext prevContext = SynchronizationContext.Current;
SynchronizationContext.SetSynchronizationContext( null );
try {
// I want the continuation for this to be posted to a thread pool
// thread, not MyCustomContext.
await Blah();
WeShouldBeInAThreadPoolThread(); // ********* this should run in a thread pool thread
} finally {
// Restore the previous SetSynchronizationContext.
SynchronizationContext.SetSynchronizationContext( prevContext );
}
}
The behavior I'm getting is that the code right after each await is executed in a seemingly-random thread. Sometimes, WeShouldBeInTheMainThreadNow() is running in a thread pool thread and sometimes the main thread. Sometimes WeShouldBeInAThreadPoolThread() is running
I don't see a pattern here, but I thought that whatever SynchronizationContext.Current was set to at the line where you use await is the one that will define where the code following the await will execute. Is that an incorrect assumption? If so, is there a compact way to do what I'm trying to do here?
I would expect your code to work, but there are a few possible reasons why it's not:
Ensure your SynchronizationContext is current when it executes its continuations.
It's not strictly defined when the SynchronizationContext is captured.
The normal way to run code in a SynchronizationContext is to establish the current one in one method, and then run another (possibly-asynchronous) method that depends on it.
The normal way to avoid the current SynchronizationContext is to append ConfigureAwait(false) to all tasks that are awaited.
There is a common misconception about await, that somehow calling an async-implemented function is treated specially.
However, the await keyword operates on an object, it does not care at all where the awaitable object comes from.
That is, you can always rewrite await Blah(); with var blahTask = Blah(); await blahTask;
So what happens when you rewrite the outer await call that way?
// Synchronization Context leads to main thread;
Task<int> xTask = SomeOtherFunction();
// Synchronization Context has already been set
// to null by SomeOtherFunction!
int x = await xTask;
And then, there is the other issue: The finally from the inner method is executed in the continuation, meaning that it is executed on the thread pool - so not only you have unset your SynchronizationContext, but your SynchronizationContext will (potentially) be restored at some time in the future, on another thread. However, because I do not really understand the way that the SynchronizationContext is flowed, it is quite possible that the SynchronizationContext is not restored at all, that it is simply set on another thread (remember that SynchronizationContext.Current is thread-local...)
These two issues, combined, would easily explain the randomness that you observe. (That is, you are manipulating quasi-global state from multiple threads...)
The root of the issue is that the await keyword does not allow scheduling of the continuation task.
In general, you simply want to specify "It is not important for the code after the await to be on the same context as the code before await", and in that case, using ConfigureAwait(false) would be appropriate;
async Task SomeOtherFunction() {
await Blah().ConfigureAwait(false);
}
However, if you absolutely want to specify "I want the code after the await to run on the thread pool" - which is something that should be rare, then you cannot do it with await, but you can do it e.g. with ContinueWith - however, you are going to mix multiple ways of using Task objects, and that can lead to pretty confusing code.
Task SomeOtherFunction() {
return Blah()
.ContinueWith(blahTask => WeShouldBeInAThreadPoolThread(),
TaskScheduler.Default);
}
Related
I'm trying to find out which approach is better let's say we have a button, after user clicks it we perform 1. Send a async request using httpClient
2. Some heavy synchronous staff like computations and saving data to a database.
Like that:
button1.Click += async(sender, e) =>
{
bool a = await Task.Run(async () => { return await MyTask1();});
}
async Task<bool> MyTask1()
{
await new HttpClient().GetAsync("https://www.webpage.com");
DoHeavyStuffFor5minutes();
return true;
}
button2.Click += async(sender, e) =>
{
bool a = await MyTask2();
}
async Task<bool> MyTask2()
{
await new HttpClient().GetAsync("https://www.webpage.com").ConfigureAwait(false);
DoHeavyStuffFor5minutes();
}
From what i understand GetAsync does not block my UI thread because underneath it uses a method which make it runs on different thread perhaps Task.Run or any other method that allows that.
But DoHeavyStuffFor5Minutes will block my UI because it will get called on the caller SynchornizationContext.
So i read that using ConfigureAwait(false) will make code after GetAsync do not run on the same SynchornizationContext as the caller. My question is, which approach is better first or the second one?
There is no need to execute HttpClient.GetAsync on a background thread using Task.Run since the HTTP request is truly asynchronous by nature so in this case your second approach is better that the first one.
When the Task returned by GetAsync has eventually finished, the remainder or MyTask2() will be executed on a thread pool thread assuming you opt out of capturing the context by calling ConfigureAwait(false).
Note however that ConfigureAwait(false) does not guarantee that the callback or remainer won't be run in the original context in all cases.
From Stephen Toub's blog post:
Does ConfigureAwait(false) guarantee the callback won’t be run in the original context?
"No. It guarantees it won’t be queued back to the original contex...but that doesn’t mean the code after an await task.ConfigureAwait(false) won’t still run in the original context. That’s because awaits on already-completed awaitables just keep running past the await synchronously rather than forcing anything to be queued back. So, if you await a task that’s already completed by the time it’s awaited, regardless of whether you used ConfigureAwait(false), the code immediately after this will continue to execute on the current thread in whatever context is still current."
So you might want to off-load DoHeavysTuffFor5minutes, which I assume is a CPU-bound and potentially long-running operation, to a background thread using Task.Run to be on the safe side. At least in the general case.
Also note that a method that is named *Async and returns a Task or Task<T> might still block the calling thread depending on its implementation. In general, this may be a reason to use your first approach of a calling both methods on a background thread in order to avoid blocking the UI thread. If you however use well-implemented APIs, such as HttpClient, this isn't an issue though.
In this code:
public async Task v_task()
{
await Task.Run(() => Console.WriteLine("Hello!"));
}
public async void v1()
{
await v_task();
// some other actions...
}
public void ButtonClick()
{
v1();
Console.WriteLine("Hi!");
}
Which methods above are actually executed in parallel in the async/await generated lower thread pool if ButtonClick is called?
I mean, what should be my concerns about race conditions working with async/await? All async methods are mandatory executed in the same caller's thread? Should I use mutex on possible shared state? If yes, how could I detect what are the shared state objects?
Which methods above are actually executed in parallel in the async/await generated lower thread pool if ButtonClick is called?
Only the Console.WriteLine within the Task.Run.
I mean, what should be my concerns about race conditions working with async/await?
I suggest you start by reading my async intro, which explains how await actually works.
In summary, async methods are usually written in a serially asynchronous fashion. Take this code for example:
CodeBeforeAwait();
await SomeOtherMethodAsync();
CodeAfterAwait();
You can always say that CodeBeforeAwait will execute to completion first, then SomeOtherMethodAsync will be called. Then our method will (asynchronously) wait for SomeOtherMethodAsync to complete, and only after that will CodeAfterAwait be called.
So it's serially asynchronous. It executes in a serial fashion, just like you'd expect it to, but also with an asynchronous point in that flow (the await).
Now, you can't say that CodeBeforeAwait and CodeAfterAwait will execute within the same thread, at least not without more context. await by default will resume in the current SynchronizationContext (or the current TaskScheduler if there is no SyncCtx). So, if the sample method above was executed in the UI thread, then you would know that CodeBeforeAwait and CodeAfterAwait will both execute on the UI thread. However, if it was executed without a context (i.e., from a background thread or Console main thread), then CodeAfterAwait may run on a different thread.
Note that even if parts of a method run on a different thread, the runtime takes care of putting any barriers in place before continuing the method, so there's no need to barrier around variable access.
Also note that your original example uses Task.Run, which explicitly places work on the thread pool. That's quite different than async/await, and you will definitely have to treat that as multithreaded.
Should I use mutex on possible shared state?
Yes. For example, if your code uses Task.Run, then you'll need to treat that as a separate thread. (Note that with await, it's a lot easier to not share state at all with other threads - if you can keep your background tasks pure, they're much easier to work with).
If yes, how could I detect what are the shared state objects?
Same answer as with any other kind of multi-threaded code: code inspection.
If you call an async function, your thread will perform this function until it reaches an await.
If you weren't using async-await, the thread would yield processing until the awaited code was finished and continue with the statement after the await.
But as you are using async-await, you told the compiler that whenever the thread has to wait for something, you have some other things it can do instead of waiting, The thread will do those other things until you say: now await until your original thing is finished.
Because of the call to an async function we are certain that somewhere inside there should be an await. Note that if you call an async function that doesn't await you get a compiler warning that the function will run synchronously.
Example:
private async void OnButton1_clickec(object sender, ...)
{
string dataToSave = ...;
var saveTask = this.MyOpenFile.SaveAsync(dataToSave);
// the thread will go into the SaveAsync function and will
// do all things until it sees an await.
// because of the async SaveAsync we know there is somewhere an await
// As you didn't say await this.MyOpenfile.SaveAsync
// the thread will not wait but continue with the following
// statements:
DoSomethingElse()
await saveTask;
// here we see the await. The thread was doing something else,
// finished it, and now we say: await. That means it waits until its
// internal await is finished and continues with the statements after
// this internal await.
Note that even if the await somewhere inside SaveAsync was finished, the thread will not perform the next statement until you await SaveTask. This has the effect that DoSomethingElse will not be interrupted if the await inside the SaveAsync was finished.
Therefore normally it's not useful to create an async function that does not return either a Task or a Task < TResult >
The only exception to this is an event handler. The GUI doesn't have to wait until your event handler is finished.
Given the following method as example:
private async void F()
{
button.IsEnabled = false;
await Task.Delay(1000);
button.IsEnabled = true;
}
In this case, any code starting at await always occurs on another thread (edit: wrong) which presumably should not have access to the UI thread, similarly to desktop apps. In a similar situation, I recall having an exception such as:
The application called an interface that was marshalled for a different thread.
However, the example does not trigger any exception. Is this expected? Can I reliably write code like this?
any code starting at await always occurs on another thread (non-UI thread, right?),
No, not at all. await does not kick off other threads. I have an async intro that may help if you find this statement confusing.
What await will do is schedule the remainder of the method as a continuation to be run after the asynchronous operation completes (in this case, the asynchronous operation is just a timer firing). By default, await will capture a "context", which is SynchronizationContext.Current (or, if it is null, the context is TaskScheduler.Current). In this case, there's a UI SynchronizationContext that ensures the remainder of the async method will run on the UI thread.
Code running on the UI thread has a SynchronizationContext. You can see that by printing SynchronizationContext.Current. Before you await something that context is captured and after the await your code resumes on that context which makes sure the continuation runs on the UI thread.
To get the behavior you're referencing, where the continuation is run on a ThreadPool thread you can disable the SynchronizationContext capturing by using ConfigureAwait(false):
private async void FooAsync()
{
button.IsEnabled = false;
await Task.Delay(1000).ConfigureAwait(false);
button.IsEnabled = true;
}
This code will raise the exception you expect.
Is this expected? Can I reliably write code like this?
Yes and yes. Code using async-await will "do the right thing" by default. But if you do want to offload something to a ThreadPool thread you can use Task.Run.
I am writing a game, and using OpenGL I require that some work be offloaded to the rendering thread where an OpenGL context is active, but everything else is handled by the normal thread pool.
Is there a way I can force a Task to be executed in a special thread-pool, and any new tasks created from an async also be dispatched to that thread pool?
I want a few specialized threads for rendering, and I would like to be able to use async and await for example for creating and filling a vertex buffer.
If I just use a custom task scheduler and a new Factory(new MyScheduler()) it seems that any subsequent Task objects will be dispatched to the thread pool anyway where Task.Factory.Scheduler suddenly is null.
The following code should show what I want to be able to do:
public async Task Initialize()
{
// The two following tasks should run on the rendering thread pool
// They cannot run synchronously because that will cause them to fail.
this.VertexBuffer = await CreateVertexBuffer();
this.IndexBuffer = await CreateIndexBuffer();
// This should be dispatched, or run synchrounousyly, on the normal thread pool
Vertex[] vertices = CreateVertices();
// Issue task for filling vertex buffer on rendering thread pool
var fillVertexBufferTask = FillVertexBufffer(vertices, this.VertexBuffer);
// This should be dispatched, or run synchrounousyly, on the normal thread pool
short[] indices = CreateIndices();
// Wait for tasks on the rendering thread pool to complete.
await FillIndexBuffer(indices, this.IndexBuffer);
await fillVertexBufferTask; // Wait for the rendering task to complete.
}
Is there any way to achieve this, or is it outside the scope of async/await?
This is possible and basically the same thing what Microsoft did for the Windows Forms and WPF Synchronization Context.
First Part - You are in the OpenGL thread, and want to put some work into the thread pool, and after this work is done you want back into the OpenGL thread.
I think the best way for you to go about this is to implement your own SynchronizationContext. This thing basically controls how the TaskScheduler works and how it schedules the task. The default implementation simply sends the tasks to the thread pool. What you need to do is to send the task to a dedicated thread (that holds the OpenGL context) and execute them one by one there.
The key of the implementation is to overwrite the Post and the Send methods. Both methods are expected to execute the callback, where Send has to wait for the call to finish and Post does not. The example implementation using the thread pool is that Sendsimply directly calls the callback and Post delegates the callback to the thread pool.
For the execution queue for your OpenGL thread I am think a Thread that queries a BlockingCollection should do nicely. Just send the callbacks to this queue. You may also need some callback in case your post method is called from the wrong thread and you need to wait for the task to finish.
But all in all this way should work. async/await ensures that the SynchronizationContext is restored after a async call that is executed in the thread pool for example. So you should be able to return to the OpenGL thread after you did put some work off into another thread.
Second Part - You are in another thread and want to send some work into the OpenGL thread and await the completion of that work.
This is possible too. My idea in this case is that you don't use Tasks but other awaitable objects. In general every object can be awaitable. It just has to implement a public method getAwaiter() that returns a object implementing the INotifyCompletion interface. What await does is that it puts the remaining method into a new Action and sends this action to the OnCompleted method of that interface. The awaiter is expected to call the scheduled actions once the operation it is awaiting is done. Also this awaiter has to ensure that the SynchronizationContext is captured and the continuations are executed on the captured SynchronizationContext. That sounds complicated, but once you get the hang of it, it goes fairly easy. What helped me a lot is the reference source of the YieldAwaiter (this is basically what happens if you use await Task.Yield()). This is not what you need, but I think it is a place to start.
The method that returns the awaiter has to take care of sending the actual work to the thread that has to execute it (you maybe already have the execution queue from the first part) and the awaiter has to trigger once that work is done.
Conclusion
Make no mistake. That is a lot of work. But if you do all that you will have less problem down the line because you can seamless use the async/await pattern as if you would be working inside windows forms or WPF and that is a hue plus.
First, realize that await introduces the special behavior after the method is called; that is to say, this code:
this.VertexBuffer = await CreateVertexBuffer();
is pretty much the same as this code:
var createVertexBufferTask = CreateVertexBuffer();
this.VertexBuffer = await createVertexBufferTask;
So, you'll have to explicitly schedule code to execute a method within a different context.
You mention using a MyScheduler but I don't see your code using it. Something like this should work:
this.factory = new TaskFactory(CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskContinuationOptions.None, new MyScheduler());
public async Task Initialize()
{
// Since you mention OpenGL, I'm assuming this method is called on the UI thread.
// Run these methods on the rendering thread pool.
this.VertexBuffer = await this.factory.StartNew(() => CreateVertexBuffer()).Unwrap();
this.IndexBuffer = await this.factory.StartNew(() => CreateIndexBuffer()).Unwrap();
// Run these methods on the normal thread pool.
Vertex[] vertices = await Task.Run(() => CreateVertices());
var fillVertexBufferTask = Task.Run(() => FillVertexBufffer(vertices, this.VertexBuffer));
short[] indices = await Task.Run(() => CreateIndices());
await Task.Run(() => FillIndexBuffer(indices, this.IndexBuffer));
// Wait for the rendering task to complete.
await fillVertexBufferTask;
}
I would look into combining those multiple Task.Run calls, or (if Initialize is called on a normal thread pool thread) removing them completely.
According to best practices it is recommended to use .ConfigureAwait(false) with async/await keywords if you can:
await Task.Run(RunSomethingAsync).ConfigureAwait(false);
Can you please give me an example of a situation when I cannot use .ConfigureAwait(false)?
You "cannot" use ConfigureAwait(false) when you actually care about the synchronization context you're in. For example, imagine the following in a GUI application:
public async void SomeButtonClick(object sender, EventArgs e)
{
var result = await SomeAsyncOperation().ConfigureAwait(false);
textBox.Text = result;
}
When you return from ConfigureAwait, you wont be back on the UI thread. This will cause an InvalidOperationException.
From the source: Asynchronous .NET Client Libraries for Your HTTP API and Awareness of async/await's Bad Effects:
When you are awaiting on a method with await keyword, compiler
generates bunch of code in behalf of you. One of the purposes of this
action is to handle synchronization with the UI (or main) thread. The key
component of this feature is the SynchronizationContext.Current which
gets the synchronization context for the current thread.
SynchronizationContext.Current is populated depending on the
environment you are in. The GetAwaiter method of Task looks up for
SynchronizationContext.Current. If current synchronization context is
not null, the continuation that gets passed to that awaiter will get
posted back to that synchronization context.
When consuming a method, which uses the new asynchronous language
features, in a blocking fashion, you will end up with a deadlock if
you have an available SynchronizationContext. When you are consuming
such methods in a blocking fashion (waiting on the Task with Wait
method or taking the result directly from the Result property of the
Task), you will block the main thread at the same time. When
eventually the Task completes inside that method in the threadpool, it
is going to invoke the continuation to post back to the main thread
because SynchronizationContext.Current is available and captured. But
there is a problem here: the UI thread is blocked and you have a
deadlock!