Given this code:
Task.Factory.StartNew(() =>
{
Application.Current.Dispatcher.Invoke(() =>
{
//Code to run on UI thread
});
//Code to run on a background thread
}).
Is it safe to assume that "Code to run on a background thread" will not be reached under any circumstances until "Code to run on UI thread" is finished executing?
When you start the Task, it will run as an worker thread and it will block until Invoke(...) is over.
When Invoke(..) is done it will continue on the worker thread.
"Code to run on UI thread" will run first, next will be "Code to run on a background thread".
You then have the possibility to do some work in a worker thread like read files, write files, query on database with out blocking the main thread, and when needed you can update UI elements with data via Invoke(...)
But take a look at Async/Await, it can make async operation and updating UI element easier to understand.
Dispatcher.Invoke Method (Delegate, Object[])
Executes the specified delegate with the specified arguments synchronously on the thread the Dispatcher is associated with.
https://msdn.microsoft.com/en-us/library/cc647509(v=vs.110).aspx
also see this:
Asynchronous vs synchronous execution, what does it really mean?
Because Dispatcher.Invoke is executed synchronously, and how synchronous execution is defined, your assumption is safe.
Related
I am using monotouch/Xamarin for an iOS app.
The documentation for Task.Run states:
Queues the specified work to run on the ThreadPool and returns a task
handle for that work.
Which essentially indicates that it could run on any thread ThreadPool.
I want to do something like:
Task.Run(async () => await PerformTask());
but have it run on the main thread. Normally I would write it using BeginInvokeOnMainThread as follows:
BeginInvokeOnMainThread(async () => await PerformTask());
But I am doing this in shared code and do not want to use iOS specific calls. Is there a way for me to tell Task.Run() to invoke the action on the main thread?
If you want to run PerformTask in the current thread, instead of a thread pool thread, you simply need to not call Task.Run. Just use:
PerformTask();
and you're done. If you want to only continue executing the rest of the method when that task is done, then await it:
await PerformTask();
There is no reason to call Task.Run here for you, nor is there any reason to create a lambda that awaits the method for no particular reason (you could just call it directly if you wanted to start it from a thread pool thread).
If you want to run a task from the main thread you could use TaskSchedluer's method FromCurrentSynchronizationContext().
Task t = new Task(() =>
{
...
});
t.Start(TaskScheduler.FromCurrentSynchronizationContext());
This can be useful sometimes. However, if you want to use this technique to bypass thread checks (for example, modifying properties of form controls in a windows app), you should rethink your design. Use tasks for calculations or other non-UI operations and update the UI from the main thread.
Have a look at MainQueue.cs: https://gist.github.com/gering/0aa9750d3c7d14b856d0ed2ba98374a8
It is for Xamarin Forms applications. You have to call Init() from main thread once, but then you are able to ensure execution on main thread.
I have a task to get products from database, and the ContinueWith action that operate some UI modification, therefore I had a problem because the Task create a new thread, and the UI modification was executed not in the UI Thread.
I tried to use this fix :
var currentScheduler = TaskScheduler.Current;
Task.Factory.StartNew(() =>
{
// get products
}).ContinueWith((x) => handleProductsArrived(x.Result, x.Exception), currentScheduler);
but it didn't work at all. I check and the ContinueWith was not executed in the thread from currentScheduler but in an another.
I discovered this method :
Task.Factory.StartNew(() =>
{
// get products
}).ContinueWith((x) => handleProductsArrived(x.Result, x.Exception), TaskScheduler.FromCurrentSynchronizationContext());
and it works. So what's the differences? Why didn't my first code work?
Thanks!
From the documentation for TaskScheduler.Current:
When not called from within a task, Current will return the Default scheduler.
Then from the Task Schedulers documentation:
The default scheduler for Task Parallel Library and PLINQ uses the .NET Framework ThreadPool to queue and execute work.
So if you use TaskScheduler.Current when you're not in a task, you'll get a scheduler which uses the thread pool.
If you call TaskScheduler.FromCurrentSynchronizationContext(), you'll get one for the current synchronization context - which in Windows Forms or WPF (when called from a UI thread) is a context which schedules work on the relevant UI thread.
So that's why the first code didn't work: it executed your continuation on a thread pool thread. Your second code executed the continuation on the UI thread.
Note that if you can use C# 5 and async/await, all of this is handled much more simply.
I need to create thread which will replace photo in Windows Forms window, than waits for ~1second and restore the previous photo.
I thought that the following code:
TaskScheduler ui = TaskScheduler.FromCurrentSynchronizationContext();
var task = Task.Factory.StartNew(() =>
{
pic.Image = Properties.Resources.NEXT;
Thread.Sleep(1000);
pic.Image = Properties.Resources.PREV;
}, CancellationToken.None, TaskCreationOptions.LongRunning, ui)
do the job, but unfortunately doesn't. It freezes main UI thread.
That's because it's not guaranteed that there is one thread per one task. One thread can be used for processing several tasks.
Even TaskCreationOptions.LongRunning option can't help.
How I can fix it?
Thread.Sleep is a synchronous delay. If you want an asynchronous delay then use Task.Delay.
In C# 5, which is at present in beta release, you can simply say
await Task.Delay(whatever);
in an asynchronous method, and the method will automatically pick up where it left off.
If you are not using C# 5 then you can "manually" set whatever code you want to be the continuation of the delay yourself.
When you pass a new TaskScheduler that is from the current synchronization context, you actually telling the task to run on the UI thread. You actually want to do that, so you can update the UI component, however you don't want to sleep on that thread, since it will block.
This is a good example of when .ContinueWith is ideal:
TaskScheduler ui = TaskScheduler.FromCurrentSynchronizationContext();
var task = Task.Factory.StartNew(() =>
{
pic.Image = Properties.Resources.NEXT;
},
CancellationToken.None,
TaskCreationOptions.None,
ui);
task.ContinueWith(t => Thread.Sleep(1000), TaskScheduler.Default)
.ContinueWith(t =>
{
pic.Image = Properties.Resources.Prev;
}, ui);
EDIT (Removed some stuff and added this):
What happens is that we're blocking the UI thread for only enough time to update pic.Image. By specifying the TaskScheduler, you're telling it what thread to run the task on. It's important to know that the relationship between Tasks and Threads is not 1:1. In fact, you can have 1000 tasks running on relatively few threads, 10 or less even, it all depends on the amount of work each task has. Do not assume each task you create will run on a separate thread. The CLR does a great job of balancing performance automatically for you.
Now, you don't have to use the default TaskScheduler, as you've seen. When you pass the UI TaskScheduler, that is TaskScheduler.FromCurrentSynchronizationContext(), it uses the UI thread instead of the thread pool, as TaskScheduler.Default does.
Keeping this in mind, let's review the code again:
var task = Task.Factory.StartNew(() =>
{
pic.Image = Properties.Resources.NEXT;
},
CancellationToken.None,
TaskCreationOptions.None,
ui);
Here, we're creating and starting a task that will run on the UI thread, that will update the Image property of pic with your resource. While it does this, the UI will be unresponsive. Fortunately, this is a likely a very fast operation, and the user won't even notice.
task.ContinueWith(t => Thread.Sleep(1000), TaskScheduler.Default)
.ContinueWith(t =>
{
pic.Image = Properties.Resources.Prev;
}, ui);
With this code, we're calling the ContinueWith method. It does exactly what it sounds like. It returns a new Task object that will execute the lambda parameter when it runs. It will be started when the task has either completed, faulted or been cancelled. You can control when it will run by passing in TaskContinuationOptions. However, we're also passing a different task scheduler as we did before. This is the default task scheduler that will execute a task on a thread pool thread, thus, NOT blocking the UI. This task could run for hours and your UI will stay responsive (don't let it), because it's a separate thread from the UI thread that you are interacting with.
We've also called ContinueWith on the tasks we've set to run on the default task scheduler. This is the task that will update the image on the UI thread again, since we've passed that same UI task scheduler to the executing task. Once the threadpool task has finished, it will call this one on the UI thread, blocking it for a very short period of time while the image is updated.
You should be using a Timer to perform a UI task at some point in the future. Just set it to run once, and with a 1 second interval. Put the UI code in the tick event and then set it off.
If you really wanted to use tasks, you'd want to have the other task not run in the UI thread but rather in a background threat (i.e. just a regular StartNew task) and then use the Control.Invoke inside of the task to run a command on the UI thread. The problem here is that is' band-aid-ing the underlying problem of starting a task just to have it sleep. Better to just have the code not even execute in the first place for the full second.
I can return anything back to the UI thread like this:
// when I am on the UI thread
var _UI = System.Threading.SynchronizationContext.Current;
// then on a background thread
_UI.Post((x) => { DoSomething(); }, null);
But, if I am on that background thread for to make it asynchrnous, then calling _UI.Post() will undo my efforts and makes the UI wait for the execution of DoSomething().
Of course _UI.PostAsync() is pseudo-code. But is there a way to execute on the UI thread but in an asynchronous way? Fundamentally, I realize I am exposing my lack of knowlege and understanding. Be merciful, please. I'm just asking ;)
SynchronizationContext.Post() will not block the background thread, as it asynchronously "posts" back to the UI thread (in this case).
The UI thread will only block for the duration of the DoSomething() call - but in this case, you're saying you must run that on the UI thread, in which case, it will tie up the UI thread.
The key here is to put most of your work on the background thread, and only call .Post(...) for operations that directly set the user interface. As such, they should be very fast operations, and never (noticably) block the UI.
I have a Backgroundworker whose purpose is to run jobs sequentially in the background. Now one job is implemented in multithreading way. That mean, the Backgroundworker will create several threads. I use Task Parallel Library so I use the Task.Factory.StartNew to create multiple Tasks.
After the tasks are run, the Backgroundworker waits all of them to finish.
Now I print the Backgroundworker's ManagedThreadID and all the tasks' ManagedThreadIDs. I found that the BackgroundWorker's ManagedThreadID is always the same as the first task's ManagedThreadID. I think this shouldn't happen so I cannot explain. I think the Backgroundworker's thread must be different to all the tasks it creates so the ManagedThreadIDs must be all different from each other.
Can anyone explain why this scenario happens? Thank you very much.
Edit:
The code is similar to this:
Backgroundworker.Run(){
// Print Thread.CurrentThread.ManagedThreadID.
var task = Task.Factory.StartNew(action1); // action1, action2 also print ManagedThredID.
taskList.Add(task);
task = Task.Factory.StartNew(action2);
taskList.Add(task);
... // Several other tasks.
foreach(var task in taskList) task.Wait();
}
You will find that one task has the same ManagedThreadID as the Backgroundworker.
I would go on a limb here and guess that the TPL is smart enough to reuse the BackgroundWorker thread. Since the worker waits for all tasks to complete running one task in the same thread is probably an optimization.
From further investigation, what you are seeing is a result of the expected behaviour of the Task.Wait method. You can read more at Task.Wait and "Inlining" on the Parallel Programming Team blog.
If the Task being Wait’d on has
already started execution, Wait has to
block. However, if it hasn’t started
executing, Wait may be able to pull
the target task out of the scheduler
to which it was queued and execute it
inline on the current thread.
The Background worker draws threads from the thread pool as well as TPL. What might happen is that the background worker is started, it draws a thread from the pool and fires TPL threads and returns immediately the thread to the pool. By the time TPL's first task is executed TPL draws a thread from the pool and it happens that it picks the same thread as the one once used by the background worker.
Of course that's just a supposition which cannot be verified because you haven't shown your code.
What you have stumbled upon is of course not a problem but a feature (optimization): The TPL is re-using threads as much as it can.
When you create a Task it is not immediately/permanently associated with a Thread. A Task is a Job that is put in a Queue and the Queue(s) are serviced by worker threads. So it could be that the Bgw Task is suspended and its thread returned to the pool, or more directly it could be done by the Wait():
// thread A
var t1 = Task.Startnew(...);
var t2 = Task.Startnew(...);
t1.Wait(); // Thread A is idle/available so Wait can execute t1
t2.Wait();
Use TaskCreationOptions.LongRunning to avoid re-cycling the background worker.