I have a async function and used the Progress< int>
to progress long process After I run another task, it seems that the many progress reports are running.
private async void Listbox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
var progress = new Progress<int>(percent =>
{
prg.Value = percent;
});
isCanceled = true;
await ExecuteManuallyCancellableTaskAsync(progress);
}
and this is my func
public async Task ExecuteManuallyCancellableTaskAsync(IProgress<int> progress)
{
var mprogress = 0;
prg.Value = 0;
using (var cancellationTokenSource = new CancellationTokenSource())
{
cancellationTokenSource.Cancel();
var SearchTask = Task.Run(async () =>
{
foreach (var file in await GetFileListAsync(GlobalData.Config.DataPath))
{
if (isCanceled)
{
cancellationTokenSource.Cancel();
return;
}
mprogress += 1;
progress.Report((mprogress * 100 / TotalItem));
await Dispatcher.InvokeAsync(() =>
{
// my codes
}, DispatcherPriority.Background);
}
});
await SearchTask;
}
}
and this is result that you can see different value in progressbar same time.
In short you are not using the CancellationTokenSource correctly for 2 reasons; firstly it needs to be passed around to any async methods that you are calling in order to truly cancel them, secondly it will likely need to live for longer than just inside the scope where you are using it.
Try something like this (complete with comments to hopefully make it easy to follow):
private CancellationTokenSource cancellationTokenSource; // And remove isCanceled as this is causing some of the issues
private async void Listbox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
var progress = new Progress<int>(percent =>
{
prg.Value = percent;
});
// Make sure any current processing is stopped.
cancellationTokenSource?.Cancel();
// Prepare to be able to cancel the next round of processing.
cancellationTokenSource = new CancellationTokenSource();
await ExecuteManuallyCancellableTaskAsync(progress, cancellationTokenSource.Token);
}
public async Task ExecuteManuallyCancellableTaskAsync(IProgress<int> progress, CancellationToken cancelToken)
{
var mprogress = 0;
prg.Value = 0;
await Task.Run(async () =>
{
// You will need to implement checks against the CancellationToken in your GetFileListAsync method also.
foreach (var file in await GetFileListAsync(GlobalData.Config.DataPath, cancelToken))
{
mprogress += 1;
progress.Report((mprogress * 100 / TotalItem));
// Only update the UI if we have not been requested to cancel.
if (!cancelToken.IsCancellationRequested)
{
await Dispatcher.InvokeAsync(() =>
{
// my codes
}, DispatcherPriority.Background);
}
}
}, cancelToken); // Pass in the token to allow the Task to be cancelled.
}
Related
I have been attempting to have a re-usable modal progress window (I.e. progressForm.ShowDialog()) to show progress from a running async task, including enabling cancellation.
I have seen some implementations that launch start the async task by hooking the Activated event handler on the form, but I need to start the task first, then show the modal dialog that will show it's progress, and then have the modal dialog close when completed or cancellation is completed (note - I want the form closed when cancellation is completed - signalled to close from the task continuation).
I currently have the following - and although this working - are there issues with this - or could this be done in a better way?
I did read that I need to run this CTRL-F5, without debugging (to avoid the AggregateException stopping the debugger in the continuation - and let it be caught in the try catch as in production code)
ProgressForm.cs
- Form with ProgressBar (progressBar1) and Button (btnCancel)
public partial class ProgressForm : Form
{
public ProgressForm()
{
InitializeComponent();
}
public event Action Cancelled;
private void btnCancel_Click(object sender, EventArgs e)
{
if (Cancelled != null) Cancelled();
}
public void UpdateProgress(int progressInfo)
{
this.progressBar1.Value = progressInfo;
}
}
Services.cs
- Class file containing logic consumed by WinForms app (as well as console app)
public class MyService
{
public async Task<bool> DoSomethingWithResult(
int arg, CancellationToken token, IProgress<int> progress)
{
// Note: arg value would normally be an
// object with meaningful input args (Request)
// un-quote this to test exception occuring.
//throw new Exception("Something bad happened.");
// Procressing would normally be several Async calls, such as ...
// reading a file (e.g. await ReadAsync)
// Then processing it (CPU instensive, await Task.Run),
// and then updating a database (await UpdateAsync)
// Just using Delay here to provide sample,
// using arg as delay, doing that 100 times.
for (int i = 0; i < 100; i++)
{
token.ThrowIfCancellationRequested();
await Task.Delay(arg);
progress.Report(i + 1);
}
// return value would be an object with meaningful results (Response)
return true;
}
}
MainForm.cs
- Form with Button (btnDo).
public partial class MainForm : Form
{
public MainForm()
{
InitializeComponent();
}
private async void btnDo_Click(object sender, EventArgs e)
{
CancellationTokenSource cts = new CancellationTokenSource();
CancellationToken token = cts.Token;
// Create the ProgressForm, and hook up the cancellation to it.
ProgressForm progressForm = new ProgressForm();
progressForm.Cancelled += () => cts.Cancel();
// Create the progress reporter - and have it update
// the form directly (if form is valid (not disposed))
Action<int> progressHandlerAction = (progressInfo) =>
{
if (!progressForm.IsDisposed) // don't attempt to use disposed form
progressForm.UpdateProgress(progressInfo);
};
Progress<int> progress = new Progress<int>(progressHandlerAction);
// start the task, and continue back on UI thread to close ProgressForm
Task<bool> responseTask
= MyService.DoSomethingWithResultAsync(100, token, progress)
.ContinueWith(p =>
{
if (!progressForm.IsDisposed) // don't attempt to close disposed form
progressForm.Close();
return p.Result;
}, TaskScheduler.FromCurrentSynchronizationContext());
Debug.WriteLine("Before ShowDialog");
// only show progressForm if
if (!progressForm.IsDisposed) // don't attempt to use disposed form
progressForm.ShowDialog();
Debug.WriteLine("After ShowDialog");
bool response = false;
// await for the task to complete, get the response,
// and check for cancellation and exceptions
try
{
response = await responseTask;
MessageBox.Show("Result = " + response.ToString());
}
catch (AggregateException ae)
{
if (ae.InnerException is OperationCanceledException)
Debug.WriteLine("Cancelled");
else
{
StringBuilder sb = new StringBuilder();
foreach (var ie in ae.InnerExceptions)
{
sb.AppendLine(ie.Message);
}
MessageBox.Show(sb.ToString());
}
}
finally
{
// Do I need to double check the form is closed?
if (!progressForm.IsDisposed)
progressForm.Close();
}
}
}
Modified code - using TaskCompletionSource as recommended...
private async void btnDo_Click(object sender, EventArgs e)
{
bool? response = null;
string errorMessage = null;
using (CancellationTokenSource cts = new CancellationTokenSource())
{
using (ProgressForm2 progressForm = new ProgressForm2())
{
progressForm.Cancelled +=
() => cts.Cancel();
var dialogReadyTcs = new TaskCompletionSource<object>();
progressForm.Shown +=
(sX, eX) => dialogReadyTcs.TrySetResult(null);
var dialogTask = Task.Factory.StartNew(
() =>progressForm.ShowDialog(this),
cts.Token,
TaskCreationOptions.None,
TaskScheduler.FromCurrentSynchronizationContext());
await dialogReadyTcs.Task;
Progress<int> progress = new Progress<int>(
(progressInfo) => progressForm.UpdateProgress(progressInfo));
try
{
response = await MyService.DoSomethingWithResultAsync(50, cts.Token, progress);
}
catch (OperationCanceledException) { } // Cancelled
catch (Exception ex)
{
errorMessage = ex.Message;
}
finally
{
progressForm.Close();
}
await dialogTask;
}
}
if (response != null) // Success - have valid response
MessageBox.Show("MainForm: Result = " + response.ToString());
else // Faulted
if (errorMessage != null) MessageBox.Show(errorMessage);
}
I think the biggest issue I have, is that using await (instead of
ContinueWith) means I can't use ShowDialog because both are blocking
calls. If I call ShowDialog first the code is blocked at that point,
and the progress form needs to actually start the async method (which
is what I want to avoid). If I call await
MyService.DoSomethingWithResultAsync first, then this blocks and I
can't then show my progress form.
The ShowDialog is indeed a blocking API in the sense it doesn't return until the dialog has been closed. But it is non-blocking in the sense it continues to pump messages, albeit on a new nested message loop. We can utilize this behavior with async/await and TaskCompletionSource:
private async void btnDo_Click(object sender, EventArgs e)
{
CancellationTokenSource cts = new CancellationTokenSource();
CancellationToken token = cts.Token;
// Create the ProgressForm, and hook up the cancellation to it.
ProgressForm progressForm = new ProgressForm();
progressForm.Cancelled += () => cts.Cancel();
var dialogReadyTcs = new TaskCompletionSource<object>();
progressForm.Load += (sX, eX) => dialogReadyTcs.TrySetResult(true);
// show the dialog asynchronousy
var dialogTask = Task.Factory.StartNew(
() => progressForm.ShowDialog(),
token,
TaskCreationOptions.None,
TaskScheduler.FromCurrentSynchronizationContext());
// await to make sure the dialog is ready
await dialogReadyTcs.Task;
// continue on a new nested message loop,
// which has been started by progressForm.ShowDialog()
// Create the progress reporter - and have it update
// the form directly (if form is valid (not disposed))
Action<int> progressHandlerAction = (progressInfo) =>
{
if (!progressForm.IsDisposed) // don't attempt to use disposed form
progressForm.UpdateProgress(progressInfo);
};
Progress<int> progress = new Progress<int>(progressHandlerAction);
try
{
// await the worker task
var taskResult = await MyService.DoSomethingWithResultAsync(100, token, progress);
}
catch (Exception ex)
{
while (ex is AggregateException)
ex = ex.InnerException;
if (!(ex is OperationCanceledException))
MessageBox.Show(ex.Message); // report the error
}
if (!progressForm.IsDisposed && progressForm.Visible)
progressForm.Close();
// this make sure showDialog returns and the nested message loop is over
await dialogTask;
}
private async void button1_Click(object sender, EventArgs e)
{
await BackupFile();
}
public async Task BackupFile()
{
await Task.Run(() =>
{
for (var i = 0; i < partCount; i++)
{
upload(FilePart);//how to creat a new task at here
//i don't want to wait here, i want to upload all part at same time
}
//wait here.
//if total part got 10, then how to wait 10 task complete
//after 10 task complete
Console.WriteLine("Upload Successful");
});
}
How to create a new task in loop and how to wait all task complete to
execute next line code
You should try task combinator WhenAll:
public async Task BackupFileAsync()
{
var uploadTasks = new List<Task>();
for (var i = 0; i < partCount; i++)
{
var uploadTask = Task.Run(() => upload(FilePart));
uploadTasks.Add(uploadTask)
}
await Task.WhenAll(uploadTasks);
Console.WriteLine("Upload Successful");
}
I have the following pieces of code:
private async void buttonStart_ItemClick(object sender, DevExpress.XtraBars.ItemClickEventArgs e)
{
Bot b = new Bot(_names);
var result = await b.Start(false, true, false);
MessageBox.Show("Done");
}
public async Task<bool> Start(bool instagram, bool twitter, bool xbox)
{
if (twitter)
{
Twitter t = new Twitter(Names);
await t.CheckNames();
}
return true;
}
public Task CheckNames()
{
List<Task> tasks = new List<Task>();
foreach (Name name in Names)
{
tasks.Add(Task.Factory.StartNew(async () =>
{
TwitterResponse result = await Check(name);
MessageBox.Show(result.msg);
}));
}
return Task.WhenAll(tasks);
}
public async Task<TwitterResponse> Check(Name name)
{
HttpClient http = new HttpClient();
HttpResponseMessage response = await http.GetAsync(string.Format("https://twitter.com/users/username_available?username={0}", name.Value));
string html = response.Content.ReadAsStringAsync().Result;
TwitterResponse result = new JavaScriptSerializer().Deserialize<TwitterResponse>(html);
return result;
}
However, I always seem to get the MessageBox saying "Done" before any of the tasks are completed.
Am I doing something wrong, how can I make sure all of the tasks actually complete before getting the messagebox?
The problem is the line tasks.Add(Task.Factory.StartNew(async () =>, you should almost never be using Task.Factory.StartNew and instead use Task.Run(.
The object that StartNew is returning is a Task<Task> which means it does not wait for the inner task to finish. You must either call .Unwrap() on the output of the StartNew before you add it to the collection or, much much better use Task.Run(
tasks.Add(Task.Run(async () =>
{
TwitterResponse result = await Check(name);
MessageBox.Show(result.msg);
}));
Which has a overload that takes in a Func<Task> and will unwrap the inner task for you.
I have this event:
private void TextBoxSearchText_TextChanged(object sender, TextChangedEventArgs e)
{
searchText();
}
and I want to cancel this parallel method and start a new one when textbox text changes and also want my textbox be responsive to my new text typing, which is lock until results come to listbox.
List<TextList> oSelected;
private void searchText()
{
string strSearchText = TextBoxSearchText.Text;
oSelected = new List<TextList>();
Parallel.ForEach(oTextList, item =>
{
Match myMatch = Regex.Match(item.EnglishText.ToString(), "\\b" + strSearchText.ToString().ToLower() + #"\w*", RegexOptions.IgnoreCase);
if (!myMatch.Success)
{
return;
}
oSelected.Add(new TextList
{
Id = item.Id,
EnglishText = item.EnglishText
});
});
ListBoxAllTexts.ItemsSource = oSelected;
}
Is it possible to use async and awiat to accomplish the job?
Which one is better for searching a text in almost 1 million line of text?
I read alot about async and await but I couldn't understand how to use it in my work.
Thank you
Since your work is CPU-bound, you should use parallel code to do the actual search. However, you can wrap the parallel work in an async/await mindset by using Task.Run:
private async void TextBoxSearchText_TextChanged(object sender, TextChangedEventArgs e)
{
ListBoxAllTexts.ItemsSource = await Task.Run(() => searchText(TextBoxSearchText.Text));
}
This will keep your UI responsive.
To do cancellation, use a CancellationTokenSource. On a side note, you can't update a List<T> from a parallel loop like you're currently trying to do because List<T> is not threadsafe. In this scenario, I recommend you use PLINQ instead:
private CancellationTokenSource _cts;
private async void TextBoxSearchText_TextChanged(object sender, TextChangedEventArgs e)
{
if (_cts != null)
_cts.Cancel();
_cts = new CancellationTokenSource();
var strSearchText = TextBoxSearchText.Text;
ListBoxAllTexts.ItemsSource = await Task.Run(
() => searchText(strSearchText, _cts.Token));
}
private List<TextList> searchText(string strSearchText, CancellationToken token)
{
try
{
return oTextList.AsParallel().WithCancellation(token)
.Where(item => Regex.IsMatch(item.EnglishText.ToString(), "\\b" + strSearchText.ToLower() + #"\w*", RegexOptions.IgnoreCase))
.Select(item => new TextList
{
Id = item.Id,
EnglishText = item.EnglishText
})
.ToList();
}
catch (OperationCanceledException)
{
return null;
}
}
Also, consider throttling the user input by only starting the search after a delay. Rx is the best way to do that.
I All,
I have to monitor an async task, which has to be cancellable and do not perform more than specific Time To Live.
I already knew about the following code.
CancellationTokenSource l_cts = new CancellationTokenSource(timemillis);
which will doing the cancellation ( as far i monitor the token in my async method).
However, this NOT gave me any information about WHY he has been cancelled, Timeout or user cancellation? furthermore, the timeout event is delayed while i did not catch the cancellation with
Token.ThrowIfCancellationRequested();
In order to solve these issues, I wrote the timeout process as follow.
static async Task TestAsync(int processDelaySeconds, int cancelDelaySeconds, int timeoutDelaySeconds )
{
CancellationTokenSource l_cts = new CancellationTokenSource();
// the process to monitor
Task l_process = new Task((state) =>
{
Console.WriteLine("Process BEGIN");
// dummy loop
for (int l_i = 0; l_i != processDelaySeconds; l_i++)
{
Thread.Sleep(1000);
l_cts.Token.ThrowIfCancellationRequested();
}
Console.WriteLine("Process END");
}, null, l_cts.Token);
// register timeout
RegisteredWaitHandle l_rwh = ThreadPool.RegisterWaitForSingleObject(l_cts.Token.WaitHandle,
(state, timedOut) =>
{
if (timedOut)
{
l_cts.Cancel();
Console.WriteLine("Timed out");
}
else
{
Console.WriteLine("Cancel Signaled");
}
},
null, (int)TimeSpan.FromSeconds(timeoutDelaySeconds).TotalMilliseconds, true);
// cancel task
if (cancelDelaySeconds > 0)
{
Task l_cancel = new Task(() =>
{
Thread.Sleep(TimeSpan.FromSeconds(cancelDelaySeconds));
l_cts.Cancel();
});
l_cancel.Start();
}
try
{
l_process.Start();
await l_process;
}
catch (OperationCanceledException)
{
Console.WriteLine("Task Cancelled");
}
finally
{
// be sure to unregister the wait handle to cancel the timeout
if (l_process.Status != TaskStatus.Canceled) l_rwh.Unregister(l_cts.Token.WaitHandle);
}
Console.WriteLine("Task Status is : {0}", l_process.Status);
}
static async void Tests()
{
Console.WriteLine("NORMAL PROCESS");
Console.WriteLine("--------------");
await TestAsync(2, 10, 10);
Console.WriteLine();
Console.WriteLine("CANCEL");
Console.WriteLine("------");
await TestAsync(5, 2, 10);
Console.WriteLine();
Console.WriteLine("TIMEOUT");
Console.WriteLine("-------");
await TestAsync(10, 15, 2);
}
Then My question is :
Is there any drawbacks or traps behind the scene ?
A better and more efficient way ??
ps- Goal is performance, not shorter code.
If you need to distinguish between the user's and time-out cancellation, you can use CreateLinkedTokenSource:
using System;
using System.Threading;
using System.Threading.Tasks;
namespace ConsoleApp
{
internal class Program
{
// worker
private static void DoWork(CancellationToken token)
{
for (int i = 0; i < 1000; i++)
{
token.ThrowIfCancellationRequested();
Thread.Sleep(100); // do the work item
}
token.ThrowIfCancellationRequested();
}
// test
private static void Main()
{
var userCt = new CancellationTokenSource();
var combinedCt = CancellationTokenSource.CreateLinkedTokenSource(
userCt.Token);
combinedCt.CancelAfter(3000); // cancel in 3 seconds
Console.CancelKeyPress += (s, e) =>
{
e.Cancel = true;
userCt.Cancel();
};
var task = Task.Run(
() => DoWork(combinedCt.Token),
combinedCt.Token);
try
{
task.Wait();
}
catch (AggregateException ex)
{
Console.WriteLine(ex.InnerException.Message);
if (task.IsCanceled)
{
if (userCt.Token.IsCancellationRequested)
Console.WriteLine("Cancelled by user");
else if (combinedCt.Token.IsCancellationRequested)
Console.WriteLine("Cancelled by time-out");
else
Console.WriteLine("Cancelled by neither user nor time-out");
}
}
}
}
}
As to your original code, you really did not need ThreadPool.RegisterWaitForSingleObject(l_cts.Token.WaitHandle, ...), there's CancellationToken.Register for that, which returns an IDisposable ready for use with using.
In order to know if your Task has been cancelled or timed out, you can use the Task.WaitAny overload which takes a TimeSpan:
// Index will return -1 if timeout has occured, otherwise will print the index of the completed task
var cnclToken = new CancellationTokenSource().Token
var yourTask = Task.Run(() => { /* Do stuff */ }, cnclToken);
var index = Task.WhenAny(new[] { yourTask }, TimeSpan.FromSeconds(1));
http://msdn.microsoft.com/en-us/library/dd235645(v=vs.110).aspx