Here is a very simple example that is available on the internet of Async/Await concept, I wonder how this little logic can be implemented using Begin/End Invoke?
All I need to do is prepare a complicated logic using both approaches (Async/Await and delegates), So I want to get a start from a basic workflow.
int countCharacters()
{
int count = 0;
using (StreamReader reader= new StreamReader("D:\\Data.txt"))
{
string content = reader.ReadToEnd();
count = content.Length;
Thread.Sleep(5000);
}
return count;
}
private async void btnProcessFIle_Click(object sender, EventArgs e)
{
Task<int> task = new Task<int>(countCharacters);
task.Start();
int count = await task;
lblCount.Text = "No. of characters in file=" +Environment.NewLine+ count.ToString();
}
Here is a very simple example that is available on the internet of Async/Await concept
This is a very bad example.
It uses the task constructor and Start, which is a clear no-no (there are literally no valid use cases to do this).
It also uses the filesystem synchronously on a background thread in an "async example".
If you want an example of how to consume synchronous (e.g., CPU-bound) code asynchronously, then this is a much better example that pretty much does the same thing:
int countCharacters()
{
Thread.Sleep(5000);
return 13;
}
private async void btnProcessFIle_Click(object sender, EventArgs e)
{
var count = await Task.Run(() => countCharacters());
lblCount.Text = "No. of characters in file=" + count;
}
Note that this is an example of how to call CPU-bound code from the UI thread asynchronously - it is not an example of the "asynchronous concept".
I wonder how this little logic can be implemented using Begin/End Invoke?
Since your work is synchronous work on a background thread, that's actually pretty easy; you can just use Delegate.BeginInvoke instead of creating your own IAsyncResult (which is the really hard part about APM - if you do need to implement it, see "Implementing the CLR Asynchronous Programming Model" in the 2007-03 issue of MSDN Magazine).
But since you can just use Delegate.BeginInvoke, it's pretty straightforward:
private void btnProcessFIle_Click(object sender, EventArgs e)
{
var ui = SynchronizationContext.Current;
Func<int> d = countCharacters;
d.BeginInvoke(CountCharactersCallback, ui);
}
private void CountCharactersCallback(IAsyncResult ar)
{
var d = (Func<int>) ((AsyncResult) ar).AsyncDelegate;
var ui = (SynchronizationContext) ar.AsyncState;
try
{
var count = d.EndInvoke(ar);
ui.Post(CountCharactersComplete, count);
}
catch (Exception ex)
{
var edi = ExceptionDispatchInfo.Capture(ex);
ui.Post(CountCharactersError, state);
}
}
private void CountCharactersComplete(object state)
{
var count = (int) state;
lblCount.Text = "No. of characters in file=" + count;
}
private void CountCharactersError(object state)
{
var edi = (ExceptionDispatchInfo)state;
edi.Throw();
}
Notes:
CountCharactersCallback is a "bare callback". Any exceptions propagated from CountCharactersCallback indicate a catastrophic error.
In particular, one must be careful not to allow exceptions from EndInvoke to propagate out of the BeginInvoke callback. This is a common mistake.
I'm using SynchronizationContext to sync back to the UI thread. This is the same behavior as await.
I'm using ExceptionDispatchInfo to preserve the exception stack trace across threads (without requiring a wrapper exception).
The CountCharactersError just raises the exception directly on the message loop. This is the same behavior as async void.
Feature BeginInvoke is access to the UI elements inside non-UI thread, so you need change your code like this:
private async void btnProcessFIle_Click(object sender, EventArgs e)
{
var task = Task.Run(() => {
var count = countCharacters();
lblCount.BeginInvoke((Action) (() =>
{
lblCount.Text = "No. of characters in file=" +Environment.NewLine+
count.ToString();
}));
});
await task;
}
Code without async / await. You don't need to use BeginInvoke because you can capture the context with TaskScheduler.FromCurrentSynchronizationContext()
int countCharacters()
{
int count = 0;
using (StreamReader reader= new StreamReader("D:\\Data.txt"))
{
string content = reader.ReadToEnd();
count = content.Length;
Thread.Sleep(5000);
}
return count;
}
private void btnProcessFIle_Click(object sender, EventArgs e)
{
Task<int> task = new Task<int>(countCharacters);
task.Start();
var uiScheduler = TaskScheduler.FromCurrentSynchronizationContext();
task.ContinueWith(count =>{
lblCount.Text = "No. of characters in file=" +Environment.NewLine+ count.ToString();
}, uiScheduler);
}
If you really want to use BeginInvoke I guess you could write
private void btnProcessFIle_Click(object sender, EventArgs e)
{
Task<int> task = new Task<int>(countCharacters);
task.Start();
var uiScheduler = TaskScheduler.FromCurrentSynchronizationContext();
task.ContinueWith(count =>{
lblCount.BeginInvoke(()=>{
lblCount.Text = "No. of characters in file=" +Environment.NewLine+ count.ToString();
});
});
}
Related
I have a WinForm, with a toolStripStatusLabel. There is a button, which spawns a new thread to perform its task. The status label needs to update during, and after this task is completed. The GUI elements are in the main thread. If I want to achieve this, can I place the relevant lines to update the label where the comments are below in the code snippet below? Also, I need to have another form open when this label is clicked. From my understanding of asynchronous coding, this should be straightforward, involving an event handler for the label, and the fact that control will return to the caller of the async method. Is this correct? I am relatively new to multithreaded and asynchronous programming, so I am quite confused.
// this is running in the main GUI thread
private async void Export_execute_Click(object sender, System.EventArgs args)
{
try
{
await System.Threading.Tasks.Task.Run(() => do_export(filename, classes, System.TimeZoneInfo.ConvertTimeToUtc(timestamp)));
// if this is successful, status label should be update (task successful)
}
catch (System.Exception e)
{
// status label should be updated (task failed)
}
}
If there is something literally awaitable in the Export method then I think to make it an async method would be better.
private async void Export_execute_Click(object sender, EventArgs e)
{
try
{
await ExportAsync("file1", "classA", DateTime.Now);
toolStripStatusLabel.Text = $"Export finished at {DateTime.Now}";
}
catch (Exception ex)
{
toolStripStatusLabel.Text = $"Export failed, {ex.ToString()}";
}
}
private async Task ExportAsync(string fileName, string classes, DateTime timestamp)
{
toolStripStatusLabel.Text = $"Export start at {timestamp}";
await Task.Delay(TimeSpan.FromSeconds(5));
toolStripStatusLabel.Text = $"Have first half done {timestamp}";
await Task.Delay(TimeSpan.FromSeconds(5));
}
private void toolStripStatusLabel_Click(object sender, EventArgs e)
{
Form2 frm2 = new Form2();
frm2.Show();
}
The standard way to report progress is to use the IProgress<T> interface. There is already an implementation of this interface that you can use (Progress<T>), and is generic so that you can supply any type of argument you want. In the example bellow the argument is a string. The key point is that the event Progress.ProgressChanged is running in the UI thread, so you don't have to worry about it.
// This will run in the UI thread
private async void Export_Execute_Click(object sender, EventArgs args)
{
try
{
var progress = new Progress<string>();
progress.ProgressChanged += ExportProgress_ProgressChanged;
// Task.Factory.StartNew allows to set advanced options
await Task.Factory.StartNew(() => Do_Export(filename, classes,
TimeZoneInfo.ConvertTimeToUtc(timestamp), progress),
CancellationToken.None, TaskCreationOptions.LongRunning,
TaskScheduler.Default);
toolStripStatusLabel.Text = $"Export completed successfully";
}
catch (Exception e)
{
toolStripStatusLabel.Text = $"Export failed: {e.Message}";
}
}
// This will run in the UI thread
private void ExportProgress_ProgressChanged(object sender, string e)
{
toolStripStatusLabel.Text = e;
}
// This will run in a dedicated background thread
private void Do_Export(string filename, string classes, DateTime timestamp,
IProgress<string> progress)
{
for (int i = 0; i < 100; i += 10)
{
progress?.Report($"Export {i}% percent done");
Thread.Sleep(1000);
}
}
How about a BackgroundWorker instead of your current Task? I prefer these because they allow easy communication between the main thread and the worker.
Note that Export_execute_Click is no longer marked as async in this scenario.
Example:
private void Export_execute_Click(object sender, System.EventArgs args) {
// Method level objects are accessible throughout this process
bool error = false;
// Process
BackgroundWorker worker = new BackgroundWorker {
WorkerReportsProgress = true
};
// This executes on main thread when a progress is reported
worker.ProgressChanged += (e, ea) => {
if (ea.UserState != null) {
// ea.UserState.ToString() contains the string progress message
}
};
// This executes as an async method on a background thread
worker.DoWork += (o, ea) => {
try {
var response = do_export(filename, classes, System.TimeZoneInfo.ConvertTimeToUtc(timestamp)));
if (response == whatever) {
worker.ReportProgress(0, "Response from do_export() was `whatever`");
} else {
worker.ReportProgress(0, "Response from do_export() was something bad");
error = true;
}
} catch (System.Exception e) {
worker.ReportProgress(0, $"do_export() failed: {e}");
}
};
// This executes on the main thread once the background worker has finished
worker.RunWorkerCompleted += async (o, ea) => {
// You can communicate with your UI normally again here
if (error) {
// You had an error -- the exception in DoWork() fired
} else {
// You're all set
}
// If you have a busy-indicator, here is the place to disable it
// ...
};
// I like to set a busy-indicator here, some sort of ajax-spinner type overlay in the main UI, indicating that the process is happening
// ...
// This executes the background worker, as outlined above
worker.RunWorkerAsync();
}
I had a timer in my app that was causing too many thread locks so I decided to replace the timer with a task that runs in a loop. Now, when using the task my UI properties are not updating correctly. I figured it was a thread issue so I move the UI updates outside of the task and used await, but the UI still isn't working. In debug I following the code to the UI and it's on the main thread with the correct data but the UI never updates. The symptom is a frozen UI and a process memory that just keep growing. I"m lost on how to fix this one. Maybe someone can help?
private static void OnStaticPropertyChanged([CallerMemberName] string propertyName = null)
{ StaticPropertyChanged?.Invoke(null, new propertyChangedEventArgs(propertyName));
}
private void PropertyChanged(object sender, PropertyChangedEventArgs e)
{
Dispatcher.BeginInvoke((Action)(() =>
{
switch (e.PropertyName)
{
case "SiderealTime":
TextLst.Content = _util.HoursToHMS(TelescopeHardware.SiderealTime);
break;
case "RightAscension":
TextRa.Content = _util.HoursToHMS(TelescopeHardware.RightAscension, "h ", "m ", "s", 3);
break;
}));
}
private static async void Main_Mount_Loop()
{
_cts = new CancellationTokenSource();
var ct = _cts.Token;
var keepGoing = true;
while (keepGoing)
{
var task = Task.Run(() =>
{
ct.ThrowIfCancellationRequested();
Task.Delay(1000, ct);
MoveAxes();
if (ct.IsCancellationRequested)
{
// Clean up here, then...
keepGoing = false;
}
}, ct);
await task;
UpdateUi();
}
}
Reactive extensions seem to be a more suited library for this usage:
Observable
.Interval(TimeSpan.FromSeconds(1), ThreadPoolScheduler.Instance)
.Do(_ => MoveAxes())
.SubscribeOn(DispatcherScheduler.Current)
.Subscribe(_ => UpdateUi());
private async Task<string> httpClient(CancellationToken cancelToken)
{
HttpClient hc = new HttpClient();
hc.Timeout = new TimeSpan(0, 0, 10);
//Task.Delay(5000).Wait(); using this one, it blocks the UI thread
//await Task.Delay(5000); using this one, it doesn't execute the task after delay
if (cancelToken.IsCancellationRequested)
{
return null;
}
HttpResponseMessage response = await hc.GetAsync(new Uri("http://google.com/"));
response.EnsureSuccessStatusCode();
string responseData = await response.Content.ReadAsStringAsync();
return responseData;
}
This is my async task and I have an issue trying to use delay inside of it. I have tried two methods and both seem to cause an issue with the task. Tried researching but couldn't find a way to fix my issue. Any help is appreciated
Other part of the code:
private async void Test()
{
string response = await httpClient(token);
Console.WriteLine("response: " + response);
}
private void button1_Click(object sender, EventArgs e)
{
Task t = new Task(Test);
t.Start();
Console.WriteLine("task started");
t.Wait();
Console.WriteLine("task finished");
}
The problem is here:
private async void Test()
{
string response = await httpClient(token);
Console.WriteLine("response: " + response);
}
As soon as you've made something async void you've completely removed any ability to track status. Your new Task(Test); is using new Task(Action), which will report completion as soon as the code first returns to the caller - i.e. at the first non-complete await (in your case: the Task.Delay). To do what you want, you should really be using the Task.Run(Func<Task>) API (or avoiding Task.Run / Task.Start completely, relying on the async plumbing itself), with a private async Task Test() method.
Your event handler could then be:
private async void button1_Click(object sender, EventArgs e)
{
Console.WriteLine("about to start task");
var t = Task.Run(Test);
Console.WriteLine("task started");
await t;
Console.WriteLine("task finished");
}
or to avoid the extra thread (as noted by Nkosi):
private async void button1_Click(object sender, EventArgs e)
{
Console.WriteLine("about to start task");
var t = Test();
Console.WriteLine("task started");
await t;
Console.WriteLine("task finished");
}
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.
Edit
I suppose the proper way of forcing await to invoke the worker asynchronously is with a Task.Run, like this:
await Task.Run(() => builder.Build(dlg.FileName, cts.Token, new Progress(ReportProgress)));
Got some light from http://blogs.msdn.com/b/pfxteam/archive/2012/04/12/10293335.aspx.
this should be easy but I'm new to async/await so bear with me. I am building a class library exposing an API with some long-running operations. In the past, I used a BackgroundWorker to deal with progress reporting and cancelation, like in this simplified code fragment:
public void DoSomething(object sender, DoWorkEventArgs e)
{
BackgroundWorker bw = (BackgroundWorker)sender;
// e.Argument is any object as passed by consumer via RunWorkerAsync...
do
{
// ... do something ...
// abort if requested
if (bw.CancellationPending)
{
e.Cancel = true;
break;
} //eif
// notify progress
bw.ReportProgress(nPercent);
}
}
and the client code was like:
BackgroundWorker worker = new BackgroundWorker
{ WorkerReportsProgress = true,
WorkerSupportsCancellation = true
};
worker.DoWork += new DoWorkEventHandler(_myWorkerClass.DoSomething);
worker.ProgressChanged += WorkerProgressChanged;
worker.RunWorkerCompleted += WorkerCompleted;
worker.RunWorkerAsync(someparam);
Now I'd like to leverage the new async pattern. So, first of all here is how I'd write a simple long-running method in my API; here I'm just reading a file line by line, just to emulate a real-world process where I'll have to convert a file format with some processing:
public async Task DoSomething(string sInputFileName, CancellationToken? cancel, IProgress progress)
{
using (StreamReader reader = new StreamReader(sInputFileName))
{
int nLine = 0;
int nTotalLines = CountLines(sInputFileName);
while ((sLine = reader.ReadLine()) != null)
{
nLine++;
// do something here...
if ((cancel.HasValue) && (cancel.Value.IsCancellationRequested)) break;
if (progress != null) progress.Report(nLine * 100 / nTotalLines);
}
return nLine;
}
}
For the sake of this sample, say this is a method of a DummyWorker class. Now, here is my client code (a WPF test app):
private void ReportProgress(int n)
{
Dispatcher.BeginInvoke((Action)(() => { _progress.Value = n; }));
}
private async void OnDoSomethingClick(object sender, RoutedEventArgs e)
{
OpenFileDialog dlg = new OpenFileDialog { Filter = "Text Files (*.txt)|*.txt" };
if (dlg.ShowDialog() == false) return;
// show the job progress UI...
CancellationTokenSource cts = new CancellationTokenSource();
DummyWorker worker = new DummyWorker();
await builder.Build(dlg.FileName, cts.Token, new Progress(ReportProgress));
// hide the progress UI...
}
The implementation for the IProgress interface comes from http://blog.stephencleary.com/2010/06/reporting-progress-from-tasks.html, so you can refer to that URL. Anyway, in this usage test the UI is effectively blocked and I see no progress. So what would be the full picture for such a scenario, with reference to the consuming code?
As noted on the top of that blog post, the information in that post is outdated. You should use the new IProgress<T> API provided in .NET 4.5.
If you're using blocking I/O, then make your core method blocking:
public void Build(string sInputFileName, CancellationToken cancel, IProgress<int> progress)
{
using (StreamReader reader = new StreamReader(sInputFileName))
{
int nLine = 0;
int nTotalLines = CountLines(sInputFileName);
while ((sLine = reader.ReadLine()) != null)
{
nLine++;
// do something here...
cancel.ThrowIfCancellationRequested();
if (progress != null) progress.Report(nLine * 100 / nTotalLines);
}
return nLine;
}
}
and then wrap it in Task.Run when you call it:
private async void OnDoSomethingClick(object sender, RoutedEventArgs e)
{
OpenFileDialog dlg = new OpenFileDialog { Filter = "Text Files (*.txt)|*.txt" };
if (dlg.ShowDialog() == false) return;
// show the job progress UI...
CancellationTokenSource cts = new CancellationTokenSource();
DummyWorker worker = new DummyWorker();
var progress = new Progress<int>((_, value) => { _progress.Value = value; });
await Task.Run(() => builder.Build(dlg.FileName, cts.Token, progress);
// hide the progress UI...
}
Alternatively, you could rewrite Build to use asynchronous APIs and then just call it directly from the event handler without wrapping it in Task.Run.