I'm trying to properly start a returned Task on a background thread so as not to block the UI thread.
The blocking call is done in another class as such:
var tcs = new TaskCompletionSource<T>();
request.BeginGetResponse(() => DoSomethingSlowThenCompleteTCS(tcs));
return tcs.Task;
I assumed I could simply start the task as such (or a million other variations I've tried:
CallThatReturnsTask()
.ContinueWith(
x =>
Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Normal,
new System.Action(() =>
{
// Some continuation stuff that needs to be on the dispatcher
}
))).Start();
base.OnActivate(); // Call that needs to run immediately
However I've found that I needed to wrap the returning task in a Task.Run() in order to not block the UI thread. I'm almost 100% certain that doing this defeats the purpose of returning the Task in the first place, but it's the only way I've gotten it working.
Task.Run(() =>
{
CallThatReturnsTask()
.ContinueWith(
x =>
Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Normal,
new System.Action(() =>
{
// Some continuation stuff that needs to be on the dispatcher
}
)));
});
base.OnActivate(); // Call that needs to run immediately
What's the correct way to go about this?
Thanks in advance.
-------------------- Edit 1 --------------------
Is this better? It still seems as if I'm wrapping a task within a task instead of just executing the first task on the correct thread.
Task.Run(() => {
var result = CallThatReturnsTask().Result;
Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Normal, new System.Action(() =>
{
// Dispatcher stuff
}
));
});
Related
Thread ThreadWindow = new Thread(async () =>
{
WindowWPF windowWPF = new WindowWPF();
windowWPF.Show();
await Task.Run(() =>
{
while (true)
{
//code
}
});
//code works when it shouldn't be available
});
For some reason, the compiler suggests changing the last line to me: }); in }){}; what is this for?
The idea is to display the loading window and work with the data in parallel, after which the window should close.
It will not, as soon as await is hit, an incomplete Task will be returned and the job within await will be returned as a future callback if it's a bit long running job.
As you have made while(true) making the piece of code to execute infinitely, assuming that you are trying to do call and forget way, If so then don't use await. Also, Instead of you creating new Thread, try making use of Task as below
var task = new Task(() => MyLongRunningMethod(),
TaskCreationOptions.LongRunning | TaskCreationOptions.PreferFairness);
task.Start();
I have some problems with Task.Factory.StartNew and Task.WaitAll. The tasks do start as they should but it looks like it just ignores the Task.WaitAll, because after clicking my button (it's the event this code is in) the MessageBox already popsup.
List<Task> tasks = new List<Task>();
if (plugin.UseProxy)
{
foreach (var item in combo)
{
Task.Factory.StartNew(() =>
{
// Some code
}).ContinueWith((t) =>
{
tasks.Add(t);
pbProgress.Value++;
}, TaskScheduler.FromCurrentSynchronizationContext());
}
}
else
{
foreach (var item in combo)
{
Task.Factory.StartNew(() =>
{
// Some code
}).ContinueWith((t) =>
{
tasks.Add(t);
pbProgress.Value++;
}, TaskScheduler.FromCurrentSynchronizationContext());
}
}
Task.WaitAll(tasks.ToArray());
MessageBox.Show("Hello");
You only add the Tasks to your List when they finish. ContinueWith executes when the task is done. So Task.WaitAll is waiting an empty list of Tasks.
So you can do this:
Task task = Task.StartNew(() =>
{
// Some code
}).ContinueWith((t) =>
{
pbProgress.Value++;
}, TaskScheduler.FromCurrentSynchronizationContext());
tasks.Add(task);
The tasks list only get the items added to it once the thread started via the StartNew finishes. The problem you are having is you are hitting the Task.WaitAll(tasks.ToArray()); before the items are added to the collection. You need to add the items to the collection in the thread that is creating them, not in the continue with,
var newTask = Task.Factory.StartNew(() =>
{
// Some code
});
tasks.Add(newTask);
newTask.ContinueWith((t) =>
{
pbProgress.Value++;
}, TaskScheduler.FromCurrentSynchronizationContext());
However your code has other problems too. You never pass in a TaskSchedueller to the factory, if you don't you can easily accidentally start your thread on the UI thread. Also I assume this code is running on the UI thread, your Task.WaitAll will block the UI thread. This can lead to deadlocks if one of those StartNew threads ended up on a UI thread.
I understand that the .Join() causes the threads to pause and wait till a thread finishes its work but how can I avoid the UI from getting frozen? This is what my codes look like"
Thread dataThread = new Thread(()=> data = getData(id));
dataThread.Start();
dataThread.Join();
Thread storingThread = new Thread(()=> storeData(data));
storingThread.Start();
I need to have the Join since the first thread returns an object containing data that needs to be stored through the second thread. But this causes a UI freeze. How can I implement these in maybe a Background thread? What do yall think I should change?
If you are using .Net framework >= 4.5 you can use Tasks
await Task.Run(() => data = getData(id));
await Task.Run(() => storeData(data));
Or in one command
await Task.Run(() => storeData(getData(id)));
If you don't have to wait till it's finished you can also do:
Task.Run(() => storeData(getData(id)));
It seems you don't need two threads:
Thread dataThread = new Thread(() => storeData(getData(id)));
dataThread.Start();
Note, that Task is preferable to Thread. Also, you probably should make use of await.
The answer has already been given. Just as an extra, I give mine.
You can also use ContinueWith like this:
Task<string>.Factory.StartNew(() => "Hey!").ContinueWith(t => Console.WriteLine(t.Result));
Put the whole work into one thread so the UI doesn't stop:
ThreadPool.QueueUserWorkItem( () => storeData(getData(id)));
Or for .Net 4
Task.Factory.StartNew(() => storeData(getData(id)));
Use the async / await keywords. Small example code:
private async void Method()
{
var result = await ExecuteAsync();
// result == true
}
private async Task<bool> ExecuteAsync()
{
//run long running action
return true;
}
In .net 4.0 you need to install Microsoft.Bcl.Async to use this feature.
A good introduction in this feature can be read on http://blog.stephencleary.com/2012/02/async-and-await.html
I have to create a method, that similar to ContinueWith(), but will execute continuation in main thread, after main Task.
How can I do that?
I could endlessly checking the state of Task in my method, and when it finishes start continuation, but I think it couldn`t work in such way:
Task<DayOfWeek> taskA = new Task<DayOfWeek>(() => DateTime.Today.DayOfWeek);
Task<string> continuation = taskA.OurMethod((antecedent) =>
{
return String.Format("Today is {0}.", antecedent.Result);
});
// Because we endlessly checking state of main Task
// Code below will never execute
taskA.Start();
So what I could do here?
Try passing around the "main" thread's Dispatcher. Example:
Task.Factory.StartNew(()=>
{
// blah
}
.ContinueWith(task=>
{
Application.Current.Dispatcher.BeginInvoke(new Action(()=>
{
// yay, on the UI thread...
}
}
Assuming that the "main" thread is UI thread. If it's not, then grab that thread's dispatcher after you make it. Use that dispatcher instead of Application.Current's (i.e. CurrentDispatcher).
You can create an ExtensionMethod for a process like this. Here is an example implementation
static class ExtensionMethods
{
public static Task ContinueOnUI(this Task task, Action continuation)
{
return task.ContinueWith((arg) =>
{
Dispatcher.CurrentDispatcher.Invoke(continuation);
});
}
}
Consume it like this.
Task run = new Task(() =>
{
Debug.WriteLine("Testing");
});
run.ContinueOnUI(() =>
{
Notify += "\nExecuted On UI"; // Notify is bound on a UI control
});
run.Start();
As i don' know about threads much i have a question.
I wanna do something in background and in background method i wanna switch back to the main thread on certain condition otherwise work in background.
How can i achieve this functionality? I am using a call to StartSyncThread from UI class(c#)
async void StartSyncThread()
{
await DoSyncAsync();
}
Task DoSyncAsync()
{
return Task.Run(() => DoSync());
}
in DoSync method i wanna switch back to main thread so that i can change UI.
Please give me a simple solution to do this. Thanks in advance!
First start your async process, then call Dispatcher.BeginInvoke to get back on the UI thread.
Task.StartNew(() =>
{
// Do Something Async
Dispatcher.BeginInvoke(() =>
{
// Update Your UI Here
});
});
Note that Dispatcher is not a static - this relies on your code being a part of a member function for a UI object, like a method on your page object.
There are a couple of approaches.
The first is to split up the synchronous method into different parts. This is best if your synchronous method calculates different types of things that go into different parts of the UI:
async Task DoSyncAsync()
{
myDataBoundUIProperty1 = await Task.Run(() => DoSync1());
myDataBoundUIProperty2 = await Task.Run(() => DoSync2());
}
The second is to use progress reporting. This is best if your UI updates are all of the same type:
Task DoSyncAsync()
{
Progress<MyProgressType> progress = new Progress<MyProgressType>(progressUpdate =>
{
myDataBoundUIProperty = progressUpdate;
});
return Task.Run(() => DoSync(progress));
}
void DoSync(IProgress<MyProgressType> progress)
{
...
if (progress != null)
progress.Report(new MyProgressType(...));
...
}
There is a final alternative, but I strongly recommend one of the two above. The two solutions above will result in a better code design (separation of concerns). The third alternative is to pass in a TaskFactory that can be used to run arbitrary code on the UI context:
Task DoSyncAsync()
{
var scheduler = TaskScheduler.FromCurrentSynchronizationContext();
var factory = new TaskFactory(scheduler);
return Task.Run(() => DoSync(factory));
}
void DoSync(TaskFactory factory)
{
...
scheduler.StartNew(() => { ... });
...
}
Again, I don't advise this last solution since it conflates your UI update logic with your background task logic. But it's better than using Dispatcher or Control directly.