Reporting Progress from Tasks - c#

Is there a way to know the status of individual Task while using the WhenAll() or WaitAll() methods of Task.
When I use await Task.WhenAll(tasks.ToArray()) for every task that completes I would like to know that the task has completed.

To report progress from an async or concurrent process use the IProgress<T> abstraction. Using the Progress<T> implmentation you can easily capture the current context and run your progress update / completion expression on the ui context while doing your work in the background:
public class WorkItem {
public async Task DoWork(IProgress<bool> completionNotification) {
await Task.Delay(TimeSpan.FromSeconds(2));
//Work Done
completionNotification.Report(true);
}
//Get, Set, Fire Property change etc
public bool Completed {
get; set;
}
}
public class ViewModel {
public async void ButtonClickHandler(object sender, EventArgs e) {
var workitemTasks = WorkItems.Select(workItem =>
workItem.DoWork(new Progress<bool>(done => workItem.Completed = done)))
.ToList();
await Task.WhenAll(workitemTasks);
}
//Get, Set, Fire Property change etc
public IEnumerable<WorkItem> WorkItems {
get; set;
}
}
More Info
Reporting Progress From Async Tasks #StephenCleary

There is probably a much nicer way to do this, but here is a raw class which takes an array of unstarted tasks and fires an event each time some of the tasks completes execution:
public class TaskBatchRunner
{
private Task[] _tasks;
public event EventHandler<Task> TaskCompleted;
public TaskBatchRunner(Task[] tasks)
{
_tasks = tasks.Select(t =>
new Task(() =>
{
t.ContinueWith(OnTaskCompleted);
t.Start();
})).ToArray();
}
public void Run()
{
foreach (var t in _tasks) t.Start();
Task.WaitAll(_tasks);
}
private void OnTaskCompleted(Task completedTask)
{
TaskCompleted?.Invoke(this, completedTask);
}
}
Usage:
var taskRunner = new TaskBatchRunner(tasks.ToArray());
taskRunner.TaskCompleted += MyTaskCompleted;
taskRunner.Run();
...
private void MyTaskCompleted(object sender, Task e)
{
System.Diagnostics.Debug.WriteLine($"task {e.Id} completed!");
}

Related

How do you get a working interaction with ConfigureAwait(true) in a console application?

In a small project i am working on i have the neccessity for a component to execute a components shutdown code in the same thread that it was initialized in. However unlike in WPF/Winforms/Web the synchronizationcontext which takes care of this does not work.
My guess is that the lack of a synchronization context is the issue that causes the lack of utilization for ConfigureAwait(true).
Does someone know how to properly implement this?
I read this article but could not make any sense of it yet. Perhaps it was too late yesterday.
Minimal Repro:
using System;
using System.Threading;
using System.Threading.Tasks;
namespace ConsoleSyncContext
{
class Program
{
static async Task Main(string[] args)
{
Console.WriteLine($"Thread: {Thread.CurrentThread.ManagedThreadId}");
await SomeBackgroundWorkAsync();
// if this is the same thread as above the question is solved.
Console.WriteLine($"Thread: {Thread.CurrentThread.ManagedThreadId}");
}
private static async Task SomeBackgroundWorkAsync()
{
await Task.Run(() => { });
}
}
}
As you already figured out, console application by default doesn't have synchronization context, so ConfigureAwait has no effect, and continuation after your await SomePageLoad() will run on random thread pool thread. Note that using async main method is essentially equivalent to this:
static async Task AsyncMain() { ... } // your `async Task Main method`
// real Main method generated by compiler
static void RealMain() {
AsyncMain().GetAwaiter().GetResult();
}
In your case you don't need any synchronization context though. What you want is initialize CefSharp on main thread and shutdown CefSharp on main thread. So instead of using async Main - you can do the same as above, but initialize and shutdown Cef outside of async method:
static void Main(string[] args) {
// starting with thread 1
Cef.Initialize(new CefSettings());
try {
AsyncMain(args).GetAwaiter().GetResult();
}
finally {
// we are on main thread here
Cef.Shutdown();
}
}
static async Task AsyncMain(string[] args) {
await SomePageLoad(); // more stuff here
}
Edit: if you insist on using synchronization context then it can be done, but will add a lot of complications for nothing. Out goal is create synchronization context which will run all actions on the same thread. This case be done with simple actions queue, here is basic implementation (don't use it in production, provided as an example only, no exception handling and so on):
class CustomSyncContext : SynchronizationContext {
private readonly BlockingCollection<WorkItem> _queue = new BlockingCollection<WorkItem>(new ConcurrentQueue<WorkItem>());
private readonly Thread _thread;
public CustomSyncContext() {
// start new thread which will handle all callbacks
_thread = new Thread(() => {
// set outselves as current sync context for this thread
SynchronizationContext.SetSynchronizationContext(this);
foreach (var item in _queue.GetConsumingEnumerable()) {
try {
// execute action
item.Action();
}
finally {
// if this action is synchronous, signal the caller
item.Signal?.Set();
}
}
});
_thread.Start();
}
public override void Post(SendOrPostCallback d, object state) {
// Post means acion is asynchronous, just queue and forget
_queue.Add(new WorkItem(() => d(state), null));
}
public override void Send(SendOrPostCallback d, object state) {
// Send means action is synchronous, wait on a single until our thread executes it
using (var signal = new ManualResetEvent(false)) {
_queue.Add(new WorkItem(() => d(state), signal));
signal.WaitOne();
}
}
public void Shutdown() {
// signal thread that no more callbacks are expected
_queue.CompleteAdding();
}
public void WaitForShutdown() {
_thread.Join();
}
private class WorkItem {
public WorkItem(Action action, ManualResetEvent signal) {
Action = action;
Signal = signal;
}
public Action Action { get; }
public ManualResetEvent Signal { get; }
}
}
And your code then becomes:
var ctx = new CustomSyncContext();
ctx.Send(async (_) => {
try {
// starting with thread 1
Cef.Initialize(new CefSettings());
// this method returns on thread 4
await SomePageLoad();
}
finally {
Cef.Shutdown();
// signal the context we are done, so that main thread can unblock
ctx.Shutdown();
Console.WriteLine("done");
}
}, null);
ctx.WaitForShutdown();
Now your code runs on custom synchronization context, and continuation after await SomePageLoad(); will be posted to that synchronization context and executed by our thread (the same thread which inited CefSharp) (no ConfigureAwait(true) is needed, as it's already true by default). Note that we achieved nothing useful - we have one more thread, and our main thread is still blocked waiting for the whole operation to complete (there is no sensible way around that).
Edit 2: here is variation which does not require separate thread, but is not much better:
class CustomSyncContext : SynchronizationContext {
private readonly BlockingCollection<WorkItem> _queue = new BlockingCollection<WorkItem>(new ConcurrentQueue<WorkItem>());
public override void Post(SendOrPostCallback d, object state) {
// Post means acion is asynchronous, just queue and forget
_queue.Add(new WorkItem(() => d(state), null));
}
public override void Send(SendOrPostCallback d, object state) {
// Send means action is synchronous, wait on a single until our thread executes it
using (var signal = new ManualResetEvent(false)) {
_queue.Add(new WorkItem(() => d(state), signal));
signal.WaitOne();
}
}
public void Shutdown() {
// signal thread that no more callbacks are expected
_queue.CompleteAdding();
}
public void Start() {
// now we run the loop on main thread
foreach (var item in _queue.GetConsumingEnumerable()) {
try {
// execute action
item.Action();
}
finally {
// if this action is synchronous, signal the caller
item.Signal?.Set();
}
}
}
private class WorkItem {
public WorkItem(Action action, ManualResetEvent signal) {
Action = action;
Signal = signal;
}
public Action Action { get; }
public ManualResetEvent Signal { get; }
}
}
static async Task Main(string[] args) {
var ctx = new CustomSyncContext();
// set sync context
SynchronizationContext.SetSynchronizationContext(ctx);
// now execute our async stuff
var task = DoStuff().ContinueWith(x => ctx.Shutdown());
// now run the loop of sync context on the main thread.
// but, how do we know when to stop? Something from outside should singal that
// in the case signal is completion of DoStuff task
// note that most of the time main thread is still blocked while waiting for items in queue
ctx.Start();
}
private static async Task DoStuff() {
try {
// starting with thread 1
Cef.Initialize(new CefSettings());
// this method returns on thread 4
await SomePageLoad();
}
finally {
Cef.Shutdown();
// signal the context we are done, so that main thread can unblock
Console.WriteLine("done");
}
}
Your problem is indeed the lack of a Synchronisation context. You can't use ConfigureAwait(true) as this implies that you need to return to the original scheduler/context which does not exist.
Custom implementation
A very simple implementation that ought to do the trick is the one found here. Basically two steps.
Implement a custom synchronization context
public class CustomSynchronizationContext : SynchronizationContext
{
public override void Post(SendOrPostCallback action, object state)
{
SendOrPostCallback actionWrap = (object state2) =>
{
SynchronizationContext.SetSynchronizationContext(new CustomSynchronizationContext());
action.Invoke(state2);
};
var callback = new WaitCallback(actionWrap.Invoke);
ThreadPool.QueueUserWorkItem(callback, state);
}
public override SynchronizationContext CreateCopy()
{
return new CustomSynchronizationContext();
}
public override void Send(SendOrPostCallback d, object state)
{
base.Send(d, state);
}
public override void OperationStarted()
{
base.OperationStarted();
}
public override void OperationCompleted()
{
base.OperationCompleted();
}
}
Initaliaze it and use it
static void Main()
{
var context = new CustomSynchronizationContext();
SynchronizationContext.SetSynchronizationContext(context);
AsyncEx library
You can also use the AsyncEx library
private static void Main(string[] args)
{
AsyncContext.Run(() => AsyncMethod(args));
}
static async void AsyncMethod(string[] args)
{
await something().ConfigureAwait(true);
}

Exiting from async infinite loops

I have initiated some async infinite loops in my WinForm application, but each time I am trying to break out of them, the program hangs up. I have read some similar topics where people suggested using CancellationTokens, but I am not able to adapt them to my needs. Here is the relevant part of my code.
static bool processStop = false;
static bool processStopped = false;
//Called once
private async void ProcessData()
{
while (!processStop)
{
await Task.Run
(
() =>
{
//Do stuff and call regular not async methods
}
);
}
processStopped = true;
}
//Button click handler to exit WinForm
btnExit.Click += (senders, args) =>
{
processStop = true;
//Programm hangs up here
while (!processStopped);
FormMain.Close();
}
Edited the code
The variables are static.
The Close method is the default Close() method for Forms.
The problem is that the call to Task.Run continues on the main thread. processStop = true; and while (!processStopped); execute synchronously one after the other. This doesn't let the ProcessData method continue its execution and a deadlock occures.
I see a couple of solutions:
Use ConfigureAwait(false) with Task.Run:
private async void ProcessData()
{
while (!processStop)
{
await Task.Run
(
() =>
{
//Do stuff and call regular not async methods
}
).ConfigureAwait(false);
}
processStopped = true;
}
This will cause the ProcessData to continue on a thread pool and you already use a thread pool by calling Task.Run, so it is not a great solution
Wrap the whole process in Task.Run:
static volatile bool processStop = false;
static volatile bool processStopped = false;
//Called once
private async void ProcessData()
{
await Task.Run(() =>
{
while (!processStop)
{
...
}
processStopped = true;
});
}
This would require changing the form of the method passed to work with the loop in it.
Make ProcessData a synchronous method to process CPU-intensive tasks and call it properly. CancellationToken would be the preferred way to cancel the task:
private void ProcessData(CancellationToken token)
{
while(!token.IsCancellationRequested)
{
// do work
}
}
And call it with this:
Task processingTask;
CancellationTokenSource cts;
void StartProcessing()
{
cts = new CancellationTokenSource();
processingTask = Task.Run(() => ProcessData(cts.Token), cts.Token);
}
btnExit.Click += async (senders, args) =>
{
cts.Cancel();
try
{
await processingTask;
}
finally
{
FormMain.Close();
}
}
If you want to spin a bunch of tasks without blocking you can do this:
using System;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace WindowsFormsApp1
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
//Called once
private async Task ProcessData()
{
int count = 0;
while (true)
{
await Task.Run
(
() =>
{
this.Invoke(new Action(() => {
label2.Text = (count++).ToString();
label1.Text = DateTime.Now.ToString(); }));
Thread.Sleep(100);
}
);
}
Debugger.Break(); //you will never see this hit at all
}
private void button1_Click(object sender, EventArgs e)
{
this.Close();
}
private async void button2_Click(object sender, EventArgs e)
{
await ProcessData();
}
}
}

Best practice on using async / await

Say I have the following class definitions:
public class Calculator
{
public CalculatorResult Calculate()
{
return LongRunningCalculation();
}
private CalculatorResult LongRunningCalculation()
{
return new CalculatorResult(0.00);
}
}
public class ClassThatUsesACalculator
{
private readonly Calculator calculator;
public ClassThatUsesACalculator()
{
this.calculator = new Calculator();
}
public void DoWork()
{
for (int i = 0; i < 10; i++)
{
var result = calculator.Calculate();
DoSomethingWithCalculationResult(result);
DoLightWork();
OnProgressChanged();
}
}
}
public partial class Form : Form
{
public Form()
{
InitializeComponent();
}
private void Method(object sender, EventArgs e)
{
DoWork();
}
private void DoWork()
{
var calculator = new ClassThatUsesACalculator();
calculator.ProgressChanged += (s, e) =>
{
// Update progressbar
};
calculator.DoWork();
}
}
If I would want to do the work done in DoWork(), on the form, asynchronously I could add a method (GetCalculationTask) that returns a task using Task.Run() and add a async eventhandler i.e. For a button (MethodOne).
Please correct me if I'm wrong, but it seems to me that this would be the only option when the ClassThatUsesACalculator and Calculator classes reside in a library I don't own.
private Task GetCalculationTask(IProgress<CalculatorProgress> progress)
{
var calculator = new ClassThatUsesACalculator();
calculator.ProgressChanged += (s, e) =>
{
progress.Report(new CalculatorProgress(0));
};
return Task.Run(() =>
{
calculator.DoWork();
});
}
private async void MethodOne(object sender, EventArgs e)
{
IProgress<CalculatorProgress> progress = new Progress<CalculatorProgress> (UpdateProgressBar);
await GetCalculationTask(progress);
}
In the case I do own the library I think there are two more options, one of which very much like the first one. Probably due to the lack of my own understanding.
Create a method on on ClassThatUsesACalculator that encapsulates the DoWork() method and then call that from an asynchronous method on the form.
or,
Encapsulate the LongRunningCalculation() on the Calculator class with a Task.Run().
public Task<CalculatorResult> CalculateAsync()
{
return Task.Run(() =>
{
return LongRunningCalculation();
});
}
Create an async method on ClassThatUsesACalculator the calls that awaits the newly created method.
public async Task DoWorkAsync()
{
for (int i = 0; i < 10; i++)
{
var result = await calculator.CalculateAsync();
DoSomethingWithCalculationResult(result);
DoLightWork();
OnProgressChanged();
}
}
Create an asynchronous method on the form (MethodThree)
private async void MethodThree(object sender, EventArgs e)
{
IProgress<CalculatorProgress> progress = new Progress<CalculatorProgress>(UpdateProgressBar);
var calculator = new ClassThatUsesACalculator();
calculator.ProgressChanged += (s, args) =>
{
progress.Report(new CalculatorProgress(0));
};
await calculator.DoWorkAsync();
}
Now, in my opinion the last option would be the best as I would remain more control. But maybe I'm way off and would like someone's opinion or pointers on this as I can only find explanations on how to consume async, but never really how to build methods for others to consume.
As a general rule, push any Task.Run usage as far up the call stack as possible.
What you want to avoid is having a method with an asynchronous signature that is implemented using Task.Run in a reusable component. That's a lying API. I have a blog post on the subject that goes into greater detail.
If you control the classes in question, I recommend using IProgress<T> instead of events for progress updates. IProgress<T> works just fine with synchronous code as well as asynchronous:
public void DoWork(IProgress<CalculatorProgress> progress = null)
{
for (int i = 0; i < 10; i++)
{
var result = calculator.Calculate();
DoSomethingWithCalculationResult(result);
DoLightWork();
if (progress != null)
progress.Report(new CalculatorProgress(...));
}
}
Then using it is quite straightforward:
private async void MethodTwo(object sender, EventArgs e)
{
IProgress<CalculatorProgress> progress = new Progress<CalculatorProgress>(UpdateProgressBar);
var calculator = new ClassThatUsesACalculator();
await Task.Run(() => calculator.DoWork(progress));
}
That keeps the Task.Run usage in the component that needs it - the UI layer - and out of the business logic.

Joining a thread started with StartNew()

When using the StartNew() method to kick off a process on a new thread, I need to figure out how to make another call into this object in that same thread (I assume this would be some sort of Join operation?).
The following example is dumbed down to illustrate the meat of what I am trying to do. I am well aware it is severely lacking in basic concurrency considerations. But I didn't want to cloud the code with all of that logic, so please forgive me on that.
The following console app shows what I am trying to accomplish. Assume on the StartNew() call a new thread with ID 9976 is created and the method invoked there. I would like the subsequent call to ProcessImmediate() in the file system watcher change event handler to be made on thread 9976 as well. As it stands, the call would share the same thread that is used for the file system watcher change event.
Can this be done, and if so, how?
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
var runner = new Runner();
runner.Run();
Console.ReadKey();
}
}
public class Runner
{
private Activity _activity = null;
private FileSystemWatcher _fileSystemWatcher;
public void Run()
{
_activity = new Activity();
// start activity on a new thread
Task.Factory.StartNew(() => _activity.Go());
_fileSystemWatcher = new FileSystemWatcher();
_fileSystemWatcher.Filter = "*.watcher";
_fileSystemWatcher.Path = "c:\temp";
_fileSystemWatcher.Changed += FileSystemWatcher_Changed;
_fileSystemWatcher.EnableRaisingEvents = true;
}
private void FileSystemWatcher_Changed(object sender, FileSystemEventArgs e)
{
// WANT TO CALL THIS FOR ACTIVITY RUNNING ON PREVIOUSLY CALLED THREAD
_activity.ProcessImmediate();
}
}
public class Activity
{
public void Go()
{
while (!Stop)
{
// for purposes of this example, magically assume that ProcessImmediate has not been called when this is called
DoSomethingInteresting();
System.Threading.Thread.Sleep(2000);
}
}
protected virtual void DoSomethingInteresting() { }
public void ProcessImmediate()
{
// for purposes of this example, assume that Go is magically in its sleep state when ProcessImmediate is called
DoSomethingInteresting();
}
public bool Stop { get; set; }
}
}
* UPDATE *
Thanks for the excellent responses. I took Mike's suggestion and implemented it for my console app. Below is the full working code which also includes the use of a cancellation token. I post this in case someone else might find it useful.
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
var runner = new Runner();
runner.Run();
Console.ReadKey();
runner.Stop();
Console.ReadKey();
}
}
public class Runner
{
private Activity _activity = null;
private FileSystemWatcher _fileSystemWatcher;
private CancellationTokenSource _cts = new CancellationTokenSource();
public void Stop() { _cts.Cancel(); }
public void Run()
{
_activity = new Activity();
// start activity on a new thread
var task = new Task(() => _activity.Go(_cts.Token), _cts.Token, TaskCreationOptions.LongRunning);
task.Start();
_fileSystemWatcher = new FileSystemWatcher();
_fileSystemWatcher.Filter = "*.watcher";
_fileSystemWatcher.Path = "C:\\Temp\\FileSystemWatcherPath";
_fileSystemWatcher.Changed += FileSystemWatcher_Changed;
_fileSystemWatcher.EnableRaisingEvents = true;
}
private void FileSystemWatcher_Changed(object sender, FileSystemEventArgs e)
{
// WANT TO CALL THIS FOR ACTIVITY RUNNING ON PREVIOUSLY CALLED THREAD
_activity.ProcessImmediate();
}
}
public class Activity : IDisposable
{
private AutoResetEvent _processing = new AutoResetEvent(false);
public void Go(CancellationToken ct)
{
Thread.CurrentThread.Name = "Go";
while (!ct.IsCancellationRequested)
{
// for purposes of this example, magically assume that ProcessImmediate has not been called when this is called
DoSomethingInteresting();
_processing.WaitOne(5000);
}
Console.WriteLine("Exiting");
}
protected virtual void DoSomethingInteresting()
{
Console.WriteLine(string.Format("Doing Something Interesting on thread {0}", Thread.CurrentThread.ManagedThreadId));
}
public void ProcessImmediate()
{
// for purposes of this example, assume that Go is magically in its sleep state when ProcessImmediate is called
_processing.Set();
}
public void Dispose()
{
if (_processing != null)
{
_processing.Dispose();
_processing = null;
}
}
}
}
First, you should use TaskCreationOptions.LongRunning if you are creating a task that will not complete quickly. Second, use an AutoResetEvent to signal the waiting thread to wake up. Note that below ProcessImmediate will return before DoSomethingInteresting has completed running on the other thread. Example:
using System.Threading;
public class Activity : IDisposable
{
private AutoResetEvent _processing = new AutoResetEvent(false);
public void Go()
{
while (!Stop)
{
// for purposes of this example, magically assume that ProcessImmediate has not been called when this is called
DoSomethingInteresting();
_processing.WaitOne(2000);
}
}
protected virtual void DoSomethingInteresting() { }
public void ProcessImmediate()
{
_processing.Set();
}
public bool Stop { get; set; }
public void Dispose()
{
if (_processing != null)
{
_processing.Dispose();
_processing = null;
}
}
}
User mike has given a better solution, which will be appropriate when you like to call the same method immediately. If you want to call a different methods immediately I'll expand mike's answer to achieve that.
using System.Threading;
public class Activity : IDisposable
{
private AutoResetEvent _processing = new AutoResetEvent(false);
private ConcurrentQueue<Action> actionsToProcess = new ConcurrentQueue<Action>();
public void Go()
{
while (!Stop)
{
// for purposes of this example, magically assume that ProcessImmediate has not been called when this is called
DoSomethingInteresting();
_processing.WaitOne(2000);
while(!actionsToProcess.IsEmpty)
{
Action action;
if(actionsToProcess.TryDeque(out action))
action();
}
}
}
protected virtual void DoSomethingInteresting() { }
public void ProcessImmediate(Action action)
{
actionsToProcess.Enqueue(action);
_processing.Set();
}
public bool Stop { get; set; }
public void Dispose()
{
if (_processing != null)
{
_processing.Dispose();
_processing = null;
}
}
}
To execute different methods on the same thread you can use a message loop that dispatches incoming requests. A simple option would be to use the event loop scheduler of the Reactive Extensions and to "recursively" schedule your Go() function - if in the mean time a different operation is scheduled it would be processed before the next Go() operation.
Here is a sample:
class Loop
: IDisposable
{
IScheduler scheduler = new EventLoopScheduler();
MultipleAssignmentDisposable stopper = new MultipleAssignmentDisposable();
public Loop()
{
Next();
}
void Next()
{
if (!stopper.IsDisposed)
stopper.Disposable = scheduler.Schedule(Handler);
}
void Handler()
{
Thread.Sleep(1000);
Console.WriteLine("Handler: {0}", Thread.CurrentThread.ManagedThreadId);
Next();
}
public void Notify()
{
scheduler.Schedule(() =>
{
Console.WriteLine("Notify: {0}", Thread.CurrentThread.ManagedThreadId);
});
}
public void Dispose()
{
stopper.Dispose();
}
}
static void Main(string[] args)
{
using (var l = new Loop())
{
Console.WriteLine("Press 'q' to quit.");
while (Console.ReadKey().Key != ConsoleKey.Q)
l.Notify();
}
}

GUI froze while Dispatcher.BeginInvoke or Task.StartNew

NOTE: Depends on this quetion
I have a view-model like this:
public class ViewModel {
private readonly IPersonService _personService;
private readonly ObservableCollection<SearchPersonModel> _foundedList;
private readonly DispatcherTimer _timer;
private readonly Dispatcher _dispatcher;
private CancellationTokenSource _tokenSource;
public SearchPatientViewModel(IPersonService personService) {
_personService = personService;
_foundedList = new ObservableCollection<SearchPersonModel>();
_dispatcher = (/*CurrentApplication*/).Dispatcher;
_timer = new DispatcherTimer(
TimeSpan.FromMilliseconds(1000),
DispatcherPriority.Normal,
TimerCallBack,
_dispatcher);
_tokenSource = new CancellationTokenSource();
}
public string Term {
get { return _term; }
set {
// implementing INotifyPropertyChanged
if(_term== value)
return;
_term= value;
OnPropertyChanged(() => Term);
tokenSource.Cancel(); // canceling prev search query
_timer.Stop(); // stop the timer to reset it
// start it again to do a search query if user change not the term for 1000ms
_timer.Start();
}
}
private void TimerCallBack(object sender, EventArgs e) {
_timer.Stop();
_tokenSource = new CancellationTokenSource();
var task = Task<IEnumerable<SearchPersonModel>>.Factory
.StartNew(Search, _tokenSource.Token);
_dispatcher.BeginInvoke((Action)(() => {
_foundedList.Clear();
foreach(var item in task.Result)
_foundedList.Add(item);
}), DispatcherPriority.Background);
}
private IEnumerable<SearchPersonModel> Search() {
return _personService.DoSearch(this.Term);
}
}
and in the IPersonService implementation I do this:
public class PersonService : IPersonService {
public IEnumerable<SearchPersonModel> DoSearch(string term){
System.Threading.Thread.Sleep(10000);
return some-search-result;
}
}
However, I expect that while search query is executing, GUI be free. But it froze! Have you any idea where is my mistake?
The problem is that evaluating task.Result will block until the query has completed.
The simplest option is probably to make the Search method perform the _dispatcher.BeginInvoke call at the end instead.
Another option - which will become easier with C# 5 - would be to add a continuation to the task, so that when it's completed you can update the UI. At the moment you'd use Task.ContinueWith; with C# 5 you'd use async and await.

Categories