Why does starting a new Task get invoked on the UI Thread - c#

Task t = new Task(() =>
{
//I would expect this to be on a worker thread, but it's not!
Thread.Sleep(1000);
});
Task test = new Task(() =>
{
Thread.Sleep(1000);
});
test.ContinueWith(x =>
{
//Do some UI Updates here, but also start another Task running.
t.Start();
}, TaskScheduler.FromCurrentSynchronizationContext());
test.Start();
Why does t gets invoked on the UI Thread. I understand that I created a continuation Task against test which correctly gets called on the UI thread, but I am then starting a new task running. Now I know I could get around this by specifying TaskScheduler.Default as an overload method against t.Start, but why does a new Task get started on the ui thread?

but why does a new Task get started on the ui thread?
Because unless specified otherwise calling Start on a task schedules the task on the current TaskScheduler which in your case is a just facade over the UI thread's SynchronizationContext that you created using TaskScheduler.FromCurrentSynchronizationContext().
Starts the Task, scheduling it for execution to the current TaskScheduler.
From Task.Start Method
If you want that task to be scheduled on a different TaskScheduler than the current one you can pass it as a parameter:
t.Start(TaskScheduler.Defualt);
Note: There's almost no case imaginable that using Task.Start is the best solution for. You should probably rethink this avenue.

Don't use the Task.Start method. Also, if you want to delay a task use Task.Delay like this:
Task.Delay(TimeSpan.FromSeconds(1)) //Wait 1 second
.ContinueWith(t => DoSomeUIWork(), TaskScheduler.FromCurrentSynchronizationContext()) //execute something on the UI thread
.ContinueWith(t => DoSomeBackgroundWork()); //Then do some background work

Related

C# Make Task Threads Foreground

I am using Task to create and perform some operations by a different thread, once the operation is done I also have called back to be called.
System.Threading.Tasks.Task.Factory.StartNew(() =>
this._httpService.CreateRecord(new Uri(Configuration.Current.CreateRecordUrl), httpObj)).ContinueWith(
(response) =>
{
if (!response.IsFaulted)
{
if (httpObj.CallBack != null)
{
httpObj.CallBack(response.Result);
}
}
else {
this._logger.Error("There was some error which causes the task to fail");
}
});
My console application's main thread is not waiting for the Task thread to complete, because it's background thread.
How can I make task thread foreground thread?
Thanks
StartNew() method returns a Task instance. Calling Wait() method on the returned task will block the main thread until the task finishes.
static void Main(string[] args)
{
var task = Task.Factory.StartNew(() =>
{
// ...
});
task.Wait(); // The main application thread waits here until the task returns
}
My console application's main thread is not waiting for the Task thread to complete, because it's background thread.
Your application is not waiting for the task, because you don't tell it to do so.
As others have already stated, use Wait/Result or await to wait for the task, depending on whether you're in an asynchronous context or not.
How can i make task thread foreground thread.
Most likely you don't want to do that in the first place. A background thread is a thread that terminates when all foreground threads have ended. Thread pool threads are inherently background threads, if you actually want to schedule your task to a foreground thread, that is, a thread that will keep the app process alive even if the main thread is finished, you'll have to create your own TaskScheduler. That, btw, would be a reason to use Task.Factory.StartNew. If you don't need Task.Factory.StartNew, go for Task.Run.
You should wait for completion of the Task in your main thread.
Change your code to
var task = System.Threading.Tasks.Task.Factory.StartNew(() =>
this._httpService.CreateRecord(new Uri(Configuration.Current.CreateRecordUrl), httpObj)).ContinueWith(
(response) =>
{
if (!response.IsFaulted)
{
if (httpObj.CallBack != null)
{
httpObj.CallBack(response.Result);
}
}
else {
this._logger.Error("There was some error which causes the task to field");
}
});
task.Wait(); // Wait till your Task has finished.
The Wait() method has some overloads to specify how long to wait. Also you have to add some exception handling if Task execution fails due to an exception of cancellation.
Try creating a fresh new Thread instead of taking from the pool. For eg:
Thread t = new Thread(()=>
{
//all your code goes here
});
t.IsBackground = false; //by default it will be foreground. so don't need this line in your case
t.Start();
This will create a foreground thread for u and it will make sure the thread completes its execution.

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);

Invoke failed in a task

I have this code :
var task = System.Threading.Tasks.Task.Factory.StartNew(() =>
{
// Code...
Invoke(new Action(() => progressBar.Increment(1)));
// Code...
});
task.Wait();
But the Invoke fail, without Exception or any kind of error.
It seems that the "wait" prevents the refresh of the form, probably because I'm still in the method.
how do I get around this?
Thanks
You got yourself a dead-lock.
When the Task is running (on a different thread) and you call Invoke, it wants to invoke it on the mainthread. But the mainthread is waiting for the Task to complete... (task.Wait() will block until it's ready)
What if you use:
await task;
The method using this code should be marked as async. The compiler will cut the method into pieces, and while waiting, the messageloop/thread runs again. When the task is ready, the rest of the method will be executed. (below the await task;)
Look here for more info: await vs Task.Wait - Deadlock?
This is a deadlock. The UI thread is blocking on the task.Wait() line, waiting for the task to finish. The task is blocking on Invoke because it needs to tell the UI thread to update the progress bar.
You should NOT be waiting for a task to complete on the UI thread, and should remove task.Wait(). The UI will update as soon as the task finishes.
Any code you were going to put below task.Wait() should go inside the Invoke action:
var task = System.Threading.Tasks.Task.Factory.StartNew(() =>
{
// do task work
Invoke(new Action(() => {
progressBar.Increment(1);
// other code that needs the task to be finished should go here
}));
});
// don't call task.Wait() here

Signaling the main thread asynchronously

I may be going about this all wrong but I'm stuck. I have a GUI application that spawns a separate thread that downloads a bunch of data from a server. When this download thread is finished I want it to send a signal to the main thread so that it knows it can now display the downloaded data.
I've tried calling Invoke (from my main form) to call a delegate to do the display work, but this blocks my downloader thread until its finished. I kind of want to just do a BeginInvoke without an EndInvoke but I know its not proper to do so.
There are a few options.
My personal favorite is to use the TPL. On your UI thread, you can make a TaskFactory, like so:
// Given:
// TaskFactory uiFactory;
uiFactory = new TaskFactory(TaskScheduler.FromCurrentSynchronizationContext());
Then, in your background task, you can just create a Task to update your UI:
var task = uiFactory.StartNew( () => UpdateUserInterface(data));
This will marshal to the UI thread correctly, similar to a BeginInvoke call. If you need to block, you can call task.Wait() (or task.Result if the Update method returns a value).
There are several options:
For WinForms use the Control.BeginInvoke method.
For WPF use the Dispatcher.BeginInvoke method.
"The TPL has other schedulers in addition to the default one and also allows you to create custom schedulers. One of the schedulers that TPL provides is based on the current synchronization context, and it can be used to ensure that my task executes on the UI thread." (Source article):
var ui = TaskScheduler.FromCurrentSynchronizationContext();
Task.Factory.ContinueWhenAll(tasks.ToArray(),
result =>
{
var time = watch.ElapsedMilliseconds;
label1.Content += time.ToString();
}, CancellationToken.None, TaskContinuationOptions.None, ui);
In the case with download scenario, .ContinueWith() continuation would be appropriate.

What is the best/safest way to access the main UI thread?

I would like to know, what is the best/safest way to access the main UI thread from another thread.
Should i use Dispatcher.BeginInvoke?
_cancelationTokenSource = new CancellationTokenSource();
new Task(() =>
{
Dispatcher.BeginInvoke((Action)(() =>
{
//process data
}));
}, _cancelationTokenSource.Token, TaskCreationOptions.LongRunning).Start();
or should i use Dispatcher.Invoke?
_cancelationTokenSource = new CancellationTokenSource();
new Task(() =>
{
Dispatcher.Invoke((Action)(() =>
{
//process data
}));
}, _cancelationTokenSource.Token, TaskCreationOptions.LongRunning).Start();
What is the main difference between the 2 Invoke methods?
What will the performance impact be when using BeginInvoke and Invoke?
Most important, i would like to keep my UI responsive.
Invoke() if you want to call it synchronous and BeginInvoke() for async .. if you use BeginInvoke you will need to pass the delegate to be called when the operation is finished.
You are already on a background thread so UI will remain responsive either way. Just depends on whether you want this thread's execution to wait for the operation to finish or if you want it done in the background while this thread's execution continues.

Categories