Is await Task.Run(ioBoundOperation) equals to ioBoundOperation? - c#

Given the pre-condition that we are on the UI thread, is
// doing other stuff
await Task.Run(Long_IO_Bound_Operation);
// do more stuff
exactly the same as:
// doing other stuff
Long_IO_Bound_Operation();
// do more stuff
By "exactly the same" i mean that the end result, including freezing (or not) of the UI are the same?
note: pls refer to https://msdn.microsoft.com/en-us/library/hh156528.aspx before answering

Task.Run will spawn a new thread and execute the Long_IO_Bound_Operation on that thread freeing up the UI thread for something else. await keyword will guarantee the execution comes back to UI thread once task that was created by Task.Run completes.
In another words, if you were to not await the TaskRun, // do more stuff would execute right after Task.Run statement. But await keyword makes sure, // do more stuff is delayed until Long_IO_Bound_Operation completes on the thread that was spawned by Task.Run
await Task.Run(SomeProcess);
DoMoreStuff();
is the same as:
Task.Run(SomeProcess).ContinueWith(t=>DoMoreStuff(), TaskScheduler.FromCurrentSynchronizationContext());
So no, it's not "exactly the same" in that the first won't block the UI thread whereas the second would.

No. Only in the sense that while Long_IO_Bound_Operation() is being awaited the UI thread will not completely lock up and have the app display "Not Responding". Exectuion will not continue until Long_IO_Bound_Operation() completes, but it won't lock the UI thread.

Related

c# storing thread reference for later termination

I'm trying to start up a thread, storing it as a reference and then terminate it on a button click. But after starting the thread and having it execute some work, the reference has false in "IsAlive" and looking at the Thread.CurrentThread doesn't give me the same as i stored on execution.
Why can't i start a thread and later abort it from the same reference as i started it from?
Thread thread = new Thread(start);
thread.Start();
Thread thread = new Thread(start);
thread.Start();
void start (){`
await something();
No longer same thread
Could somebody explain why this is happening?
Thanks in advance!
In my start method i'm simply awaiting a list of tasks. I'm assuming these are executed by the thread i started the start method from, right?
Wrong.
When an await happens on an incomplete awaitable (typically: Task / Task<T> or ValueTask<T>), it registers a continuation and returns. If you return to the top of the call stack: your thread completes.
Separately, when the awaitable completes, the continuation will be invoked. This could be synchronously on the thread that is doing the completion, or it could be via a scheduler such as the sync-context.
The one thing it can't run on is the thread that did the await: because that thread has already finished.

What code is actually executed "multi-threadedly" in async/await pattern?

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.

Why can new threads access the UI?

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.

starts new thread inside Task.ContinueWith

i am building a WPF app where it needs job1 runs in the background; when job1 ends, it switches back to the UI thread using Task.ContinueWith and makes changes to the UI according to job1's result (in my case, starts another popup) and starts job2 in the background.
The problem is that, while job1 runs in a separate thread as it should, job2 runs in the UI thread (it blocks popup2). Maybe because job2 is started inside the ContinueWith block of task1? But why? It worked the first time, why not second time? How to fix this problem?
Thanks in advance!
enterPop(PopupTypes.popup1);
var task1 = new Task(() => job1()); //task runs in another thread
task1.ContinueWith(previousTask =>
{
//**back in UI thread**
//check result of job 1, start another popup in UI
exitPopup();
enterPop(PopupTypes.popup2);
//**SUPPOSEDLY** starts another background thread for task 2
var task2 = new Task(()=> job2());
task2.ContinueWith(previousTask =>{
//do task in UI thread...
},TaskScheduler.FromCurrentSynchronizationContext());
task2.Start();
}, TaskScheduler.FromCurrentSynchronizationContext());
task.Start();
It's not clear to me what the enterPop() and exitPopup() methods are supposed to do, as the code example is missing that detail. Maybe they are displaying some kind of window in the GUI? It's also not clear what code exists in the "check result of job 1" part, "do task in UI thread", etc.
Without these details, it's impossible to know what your code is actually doing.
In any case, Hans's advice is correct. It's not really clear from the incomplete code example why you get the behavior you're seeing (for that matter, it's not really entirely clear to me what behavior you're seeing). But it's likely that if you'd written the code in the more usual async/await pattern, it would work fine:
enterPop(PopupTypes.popup1);
await Task.Run(() => job1()); //task runs in another thread
//**back in UI thread**
//check result of job 1, start another popup in UI
exitPopup();
enterPop(PopupTypes.popup2);
// starts another background thread for task 2
await Task.Run(()=> job2());
exitPopup();
//do task in UI thread...
For bonus points, have the job1() and job2() methods return the result that you check (per the commend in your code), and use something like var result = await Task.Run(() => job1()); to retrieve the value (instead of whatever mechanism you're using now).
In some cases TaskScheduler may decide to execute Task in the current thread, e.g. read the following example from Jeffrey Richter's book CLR via c#:
When a thread calls the Wait method, the system checks if
the Task that the thread is waiting for has started executing. If it
has, then the thread calling Wait will block until the Task has
completed running. But if the Task has not started executing yet, then
the system may (depending on the TaskScheduler) execute the Task using
the thread that called Wait. If this happens, then the thread calling
Wait does not block; it executes the Task and returns immediately
I believe something similar happens in your logic. TaskScheduler just follows your task queue and decides to execute it syncronously. The easiest way out is to start task2 in default scheduler
task2.Start(TaskScheduler.Default);

How does the runtime know when to spawn a thread when using "await"?

EDIT
I took Jon's comment and retried the whole thing. And indeed, it is blocking the UI thread. I must have messed up my initial test somehow. The string "OnResume exits" is written after SomeAsync has finished. If the method is changed to use await Task.WhenAll(t) it will (as expected) not block. Thanks for the input!
I was first thinking about deleting the question because the initial assumption was just wrong but I think the answers contains valuable information that should not be lost.
The original post:
Trying to understand the deeper internals of async-await. The example below is from an Android app using Xamarin. OnResume() executes on the UI thread.
SomeAsync() starts a new task (= it spawns a thread). Then it is using Task.WaitAll() to perform a blocking wait (let's not discuss now if WhenAll() would be a better option).
I can see that the UI is not getting blocked while Task.WaitAll() is running. So SomeAsync() does not run on the UI thread. This means that a new thread was created.
How does the await "know" that it has to spawn a thread here - will it always do it? If I change the WaitAll() to WhenAll(), there would not be a need for an additional thread as fast as I understand.
// This runs on the UI thread.
async override OnResume()
{
// What happens here? Not necessarily a new thread I suppose. But what else?
Console.WriteLine ("OnResume is about to call an async method.");
await SomeAsync();
// Here we are back on the current sync context, which is the UI thread.
SomethingElse();
Console.WriteLine ("OnResume exits");
}
Task<int> SomeAsync()
{
var t = Task.Factory.StartNew (() => {
Console.WriteLine("Working really hard!");
Thread.Sleep(10000);
Console.WriteLine("Done working.");
});
Task.WhenAll (t);
return Task.FromResult (42);
}
Simple: it never spawns a thread for await. If the awaitable has already completed, it just keeps running; if the awaitable has not completed, it simply tells the awaitable instance to add a continuation (via a fairly complex state machine). When the thing that is being completed completes, that will invoke the continuations (typically via the sync-context, if one - else synchronously on the thread that is marking the work as complete). However! The sync-context could theoretically be one that chooses to push things onto the thread-pool (most UI sync-contexts, however, push things to the UI thread).
I think you will find this thread interesting: How does C# 5.0's async-await feature differ from the TPL?
In short, await does not start any threads.
What it does, is just "splitting" the code into at the point where the, let's say, line where 'await' is placed, and everything that that line is added as continuation to the Task.
Note the Task. And note that you've got Factory.StartNew. So, in your code, it is the Factory who actually starts the task - and it includes placing it on some thread, be it UI or pool or any other task scheduler. This means, that the "Task" is usually already assigned to some scheduler when you perform the await.
Of course, it does not have to be assigned, nor started at all. The only important thing is that you need to have a Task, any, really.
If the Task is not started - the await does not care. It simply attaches continuation, and it's up to you to start the task later. And to assign it to proper scheduler.

Categories