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.
Related
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();
I want to start long-running operation like requesting a web page from ViewModel, and perform some progress-update operations on my View. Before, I easily achieved this by awaiting my Model's async methods, but in current project I'm restricted with .NET 4.0, so I can't use C#5 features.
What is the recommended way of doing this?
Use this -
Task.Factory.StartNew(() =>
{
// Code to load web page or any code you want to run asynchronously
}).ContinueWith(task =>
{
// Code you want to execute on completion of the above synchronous task,
}, UIHelper.GetUITaskScheduler());
wherein the UIHelper class has the following static method -
public class UIHelper
{
/* Some other methods */
public static TaskScheduler GetUITaskScheduler()
{
TaskScheduler scheduler = null;
Application.Current.Dispatcher.Invoke(() =>
{
scheduler = TaskScheduler.FromCurrentSynchronizationContext();
});
return scheduler;
}
}
use RX
https://rx.codeplex.com/
You can also observe on the SyncronizationContext.Current
Check them out...
public async override void InitData()
{
_domainModel = new DomainModel()
ProgressIndicatorViewModel.Start();
_State = Getstate();
await _domainModel.Load(_State, ProgressIndicatorViewModel); //returns a task
ImageSelectionViewModel.UpdateState(_State); //returns void not a task!
ProgressIndicatorViewModel.Stop();
DispatcherHelper.UIDispatcher.Invoke(() => ImageSelectionViewModel.RefreshImages(_imageList));
}
i would like to make the two methods (1.domaminModel.Load(), 2. UpdateState()) to run on the same thread one after the other. not on the UI thread.
how can i do this?
i would like to make the two methods (1.domaminModel.Load(), 2.
UpdateState()) to run on the same thread one after the other. not on
the UI thread.
Updated, if you want only _domainModel.Load and ImageSelectionViewModel.UpdateState to run on a separate non-UI thread, then just do this:
public async override void InitData()
{
_domainModel = new DomainModel()
ProgressIndicatorViewModel.Start();
_State = Getstate();
await Task.Run(async () =>
{
await _domainModel.Load(_State, ProgressIndicatorViewModel))
ImageSelectionViewModel.UpdateState(_State); //returns void not a task!
});
ProgressIndicatorViewModel.Stop();
ImageSelectionViewModel.RefreshImages(_imageList);
}
Note you do not need DispatcherHelper.UIDispatcher.Invoke() wrapper then.
If you want the rest of InitData after _domainModel.Load to run on a separate thread:
public async override void InitData()
{
_domainModel = new DomainModel()
ProgressIndicatorViewModel.Start();
_State = Getstate();
await Task.Run(() => _domainModel.Load(_State,
ProgressIndicatorViewModel)).ConfigureAwait(false);
ImageSelectionViewModel.UpdateState(_State); //returns void not a task!
DispatcherHelper.UIDispatcher.Invoke(() =>
{
ProgressIndicatorViewModel.Stop();
ImageSelectionViewModel.RefreshImages(_imageList)
});
}
Note that Task.Run will automatically unwrap the nested task (of Task<Task<T>>) for your here. Also, you'd probably need to move ProgressIndicatorViewModel.Stop() inside Dispatcher.Invoke.
Depending on what's inside _domainModel.Load, you may not even need Task.Run:
await _domainModel.Load(_State,
ProgressIndicatorViewModel).ConfigureAwait(false);
One a side note, you should probably handle exceptions inside your async void InitData method. You won't be able to handle them outside it.
Consider following codes:
public async void Sample()
{
DoOne();
await DoTwo();
DoThree();
Dispather.BeginInvoke(() => DoFour());
}
DoOne Will not executed in UI Thread (!), its thread will be a thread that is a caller of method Sample() named Caller Thread
In most cases in client side the Caller thread is UI Thread, because most codes are invoked by user actions (such as mouse click), but it's not a fixed behavior.
DoTwo will be executed in a separated thread, it's clear that it will not be executed in caller thread.
DoThree will be executed in a Caller Thread
and DoFour will be executed at UI Thread, it does not matter that what's a caller thread.
So the comment of #usr is totally wrong.
To achieve your goal change your code as following:
public async override void InitData()
{
_domainModel = new DomainModel()
ProgressIndicatorViewModel.Start();
_State = Getstate();
await Task.Factory.StartNew(() => {
_domainModel.Load(_State, ProgressIndicatorViewModel).ContinueWith(() => {
ImageSelectionViewModel.UpdateState(_State); });
};
ProgressIndicatorViewModel.Stop();
DispatcherHelper.UIDispatcher.Invoke(() => ImageSelectionViewModel.RefreshImages(_imageList));
}
I'm trying to resolve a problem where my UI is being blocked and I don't understand why.
public Task AddStuff(string myID, List<string> otherIDs)
{
Action doIt = () =>
{
this.theService.AddStuff(myID, otherIDs);
};
return Task.Factory.StartNew(doIt, TaskCreationOptions.LongRunning);
}
If the list is long the call can take 30 seconds and the entire application becomes unresponsive (goes that washed out white in Windows 7).
Is there a different way to do this so it doesn't block the UI?
Edit
Ok, so there's a LOT of code around this I'm going to try to keep this pertinent. I did realize going back to the original code, that I had removed something that may have been important. Should I maybe use a different TaskScheduler than TaskScheduler.Current?
Also there are no Wait statements impeding any of this code, and the service doesn't interact with the UI.
Task.Factory.StartNew(objState =>
{
LoadAssets(objState);
}, state, this.cancellationToken, TaskCreationOptions.LongRunning, TaskScheduler.Current);
private void LoadAssets(object objState)
{
LoadAssetsState laState = (LoadAssetsState)objState;
List<string> assetIDs = new List<string>();
for (int i = 0; i < laState.AddedMediaItems.Count; i++)
{
if (laState.CancellationToken.IsCancellationRequested)
return;
string assetId = this.SelectFilesStep.AssetService.GetAssetId(laState.AddedMediaItems[i], laState.ActiveOrder.OrderID);
assetIDs.Add(assetId);
}
if (laState.CancellationToken.IsCancellationRequested)
return;
this.ApiContext.AddAssetToProduct(laState.ActiveOrder.OrderID, laState.ActiveProduct.LineID, assetIDs, laState.Quantity, laState.CancellationToken).ContinueWith(task =>
{
if (laState.CancellationToken.IsCancellationRequested)
return;
App.ApiContext.GetOrderDetails(laState.ActiveOrder.OrderID, false, laState.CancellationToken).ContinueWith(orderDetailsTask =>
{
if (laState.CancellationToken.IsCancellationRequested)
return;
this.activeOrder = orderDetailsTask.Result;
this.StandardPrintProductsStep.Synchronize(this.activeOrder);
});
});
}
public Task AddAssetToProduct(string orderID, string lineID, List<string> assetIDs, int quantity, CancellationToken? cancellationToken = null)
{
Action doIt = () =>
{
if (cancellationToken.IsCancellationRequested())
return;
this.ordersService.AddAssetToProduct(orderID, lineID, assetIDs, quantity);
};
if (cancellationToken != null)
return Task.Factory.StartNew(doIt, cancellationToken.Value, TaskCreationOptions.LongRunning, TaskScheduler.Current);
else
return Task.Factory.StartNew(doIt, TaskCreationOptions.LongRunning);
}
EDIT
I have placed break points just before and after the service call and it is the service call that is blocking the UI, as opposed to any other line.
It sounds like there is no reason this should be blocking, so I think I'm just going to break the list down if it's long and make multiple calls. I just wanted to make sure I wasn't missing something here with my Task logic.
Is there a different way to do this so it doesn't block the UI?
This call, in and of itself, should not block the UI. If, however, theService.AddStuff does some synchronization with the UI's SynchronizationContext, this could cause the UI to effectively be blocked by that call.
Otherwise, the problem is likely happening from outside of this function. For example, if you call Wait() on the task returned from this method, in a UI thread, the UI thread will be blocked until this completes.
You probably want to use TaskScheduler.Default, not TaskScheduler.Current. If this is being called within a Task that's scheduled on a TaskScheduler based on the UI thread, it will schedule itself on the UI thread.
Wish I could put formatted code in a comment, but since I don't see how, adding this snippet as an answer. This is the kind of approach I'd use to figure out whether the task is running on the UI thread or not (since you don't want it to) and have the action be something completely different (a simple thread.sleep).
var state = new object();
var cancellationTokenSource = new CancellationTokenSource();
var cancellationToken = cancellationTokenSource.Token;
var task = Task.Factory.StartNew(
objState => { Console.WriteLine ("Current thread is {0}", Thread.CurrentThread.ManagedThreadId); Thread.Sleep(30); },
state,
cancellationToken,
TaskCreationOptions.LongRunning,
TaskScheduler.Current);
task.Wait();
With the code below, the final UI updates made in the final ContinueWith never take place. I think it is because of the Wait() I have at the end.
The reason I am doing that is because without the Wait, the method will return the IDataProvider before its finished being constructed in the background.
Can someone help me get this right?
Cheers,
Berryl
private IDataProvider _buildSQLiteProvider()
{
IDataProvider resultingDataProvider = null;
ISession session = null;
var watch = Stopwatch.StartNew();
var uiContext = TaskScheduler.FromCurrentSynchronizationContext();
// get the data
var buildProvider = Task.Factory
.StartNew(
() =>
{
// code to build it
});
// show some progress if we haven't finished
buildProvider.ContinueWith(
taskResult =>
{
// show we are making progress;
},
CancellationToken.None, TaskContinuationOptions.None, uiContext);
// we have data: reflect completed status in ui
buildProvider.ContinueWith(
dataProvider =>
{
// show we are finished;
},
CancellationToken.None, TaskContinuationOptions.OnlyOnRanToCompletion, uiContext);
try {
buildProvider.Wait();
}
catch (AggregateException ae)
{
foreach (var e in ae.InnerExceptions)
Console.WriteLine(e.Message);
}
Console.WriteLine("Exception handled. Let's move on.");
CurrentSessionContext.Bind(session);
return resultingDataProvider;
}
====
just to be clear
I am not having trouble talking to the ui thread. The first continue with updates the ui just fine. The trouble I am having is the timing of the last ui update and the return of the data provider.
I commented out some of the code to reduce the noise level in tis post and focus on the task sequencing.
====
ok, working code
private void _showSQLiteProjecPicker()
{
var watch = Stopwatch.StartNew();
var uiScheduler = TaskScheduler.FromCurrentSynchronizationContext();
ISession session = null;
// get the data
var buildProvider = Task.Factory.StartNew(
() =>
{
var setProgress = Task.Factory.StartNew(
() =>
{
IsBusy = true;
Status = string.Format("Fetching data...");
},
CancellationToken.None, TaskCreationOptions.None, uiScheduler);
var provider = new SQLiteDataProvider();
session = SQLiteDataProvider.Session;
return provider;
});
buildProvider.ContinueWith(
buildTask =>
{
if(buildTask.Exception != null) {
Console.WriteLine(buildTask.Exception);
}
else {
Check.RequireNotNull(buildTask.Result);
Check.RequireNotNull(session);
_updateUiTaskIsComplete(watch);
CurrentSessionContext.Bind(session);
var provider = buildTask.Result;
var dao = provider.GetActivitySubjectDao();
var vm = new ProjectPickerViewModel(dao);
_showPicker(vm);
}
},
CancellationToken.None, TaskContinuationOptions.OnlyOnRanToCompletion, uiScheduler);
}
UPDATE BELOW
This code doesn't look like it warrants TPL to me. Looks like maybe a good use for a BackgroundWorker instead!
Either way, the updates are probably not taking place because you can't update the UI from a separate thread -- you need to run the update on the UI thread. You should use the Dispatcher for this (http://stackoverflow.com/questions/303116/system-windows-threading-dispatcher-and-winforms contains info for both WPF and WinForms)
Update:
So I obviously missed some of the code so here's a revised answer. First of all, Nicholas is correct -- .ContinueWith returns a new task (http://msdn.microsoft.com/en-us/library/dd270696.aspx). So instead of
var result = Task.Factory.StartNew(...);
result.ContinueWith(...);
you probably want to create a new task and then make all the ContinueWith() calls and assign to the task and then call .Start() on the task. Something like:
var task = new Task(...).ContinueWith(...);
task.Start();
However, there is a flaw in the design to begin with (as I see it)! You're trying to run this code async, wihch is why you're using threads and TPL. However, you're calling buildProvider.Wait(); on the UI thread which blocks the UI thread until this task completes! Aside from the issue of repainting the UI in the ContinueWith() while the UI thread is blocked, there's no benefit to multithreading here since you're blocking the UI thread (a major no-no). What you probably want to do is stick the Bind()-ing inside a ContinueWith or something so that you don't have to call Wait() and block the UI thread.
My $0.02 is that if you expect the query to take a long time what you really want is 2 threads (or tasks in TPL)-- one to perform the query and one to update the UI at intervals with status. If you don't expect it to take so long I think you just want a single thread (Task) to query and then update the UI when it's done. I would probably do this via BackgroundWorker. TPL was built for managing lots of tasks and continuations and such but seems overkill for this kind of thing -- I think you could do it using a BackgroundWorker in a lot less code. But you mention you want to use TPL which is fine, but you're going to have to rework this a bit so that it actually runs in the background!
PS - you probably meant to put the Console.WriteLine("Exception handled. Let's move on."); inside the catch
I'm a little hazy, but last time I used the TPL I found it confusing. ContinueWith() returns a new Task instance. So you need to assign the second ContinueWith() result to a new variable, say var continuedTask = builderProvider.ContinueWith(...), and then change the last one to reference continuedTask.ContinueWith() instead of buildProvider.ContinueWith(). Then Wait() on the last Task.
Hope that helps!