is wrapper necessary in async await structure - c#

I'm relatively new to C# and I've been learning asynchronous programming. So I've been trying to make a simple UI with a label and a button and when I press the button, the label text changes and I've been taught to do it like this:
private void button1_Click(object sender, EventArgs e)
{
CallChangeLable();
label1.Text = "Loading ...";
}
private async void CallChangeLable()
{
var result = await ChangeLabelAsync("Oliver");
label1.Text = result;
}
private Task<string> ChangeLabelAsync(string name)
{
return Task.Run(() => ChangeLable(name));
}
private string ChangeLable(string name)
{
Thread.Sleep(2000);
return $"Hi {name}";
}
now I've tried this code without the wrapper step:
private Task<string> ChangeLabelAsync(string name)
{
return Task.Run(() => ChangeLable(name));
}
Of course I moved the task.run method to somewhere else and I modified the naming and the code still works fine, the UI is still responsive. My question is, why is the wrapper step important and can I skip it?
Here's the modified code:
private void button1_Click(object sender, EventArgs e)
{
CallChangeLable();
label1.Text = "Loading ...";
}
private async void CallChangeLable()
{
var result = await Task.Run(() => ChangeLable("Mahmoud"));
label1.Text = result;
}
private string ChangeLable(string name)
{
Thread.Sleep(2000);
return $"Hi {name}";
}

My question is, why is the wrapper step important and can I skip it?
Actually, you shouldn't have a wrapper. Providing a fake-asynchronous method is an antipattern.

using SynchronizationContext you can do something like this
private async void button1_Click(object sender, EventArgs e)
{
SynchronizationContext.Current.Post((txt)=>{
label1.Text = txt;
},"Loading....");
}

Well, you want to ensure that you don't block the UI thread - So don't skip it.
Stephen Cleary has some excellent articles on the subject:
https://blog.stephencleary.com/2013/11/taskrun-etiquette-examples-dont-use.html
For Async/Await programming in general take a look at the best practices from an old msdn post also by Stephen Cleary:
https://msdn.microsoft.com/en-us/magazine/jj991977.aspx
Hope that helps.

Related

C# WPF Update Label before and after processing - immediately

I have already tried several online examples (Thread, Dispatcher, await/async) but none is working for me in my C#/WPF project.
I have the following button click method:
private void BtnInstall_Click(object sender, RoutedEventArgs e)
{
this.lblResponse.Content = "";
executeInstall(); //do some work
this.lblResponse.Content = "DONE";
}
The label gets updated afterwards to DONE, but when I click again on the button the label isnt getting emptied before the processing of executeInstall.
As I mentioned I already tried several different examples from other questions (Dispatcher.BeginInvoke, Thread, Task, await/async) but none of them has worked - the label change before is never done before the processing of executeInstall.
I am working in .NET framework 4.7.2.
Is there maybe a setting that debug mode only executes the program with one thread and thats maybe why none of the solutions works for me?
Use async for that.
private async void BtnInstall_Click(object sender, RoutedEventArgs e)
{
this.lblResponse.Content = "";
await Task.Run(()=> executeInstall());
this.lblResponse.Content = "DONE";
}
UPDATE: If you need to access the UI inside your executeIntall method you will need to invoke the Dispatcher. In this case you would need to delay the Task to give the label time to update before the install starts. Note that this will cause the UI to freeze during the entire install.
private async void BtnInstall_Click(object sender, RoutedEventArgs e)
{
lblResponse.Content = "starting...";
await Task.Delay(100).ContinueWith(_=>
{
App.Current.Dispatcher.Invoke(() =>
{
executeInstall();
lblResponse.Content = "DONE";
});
});
}
A better approach would be to only call the dispatcher when it's actually needed. This would keep the UI responsive during the entire process.
private async void BtnInstall_Click(object sender, RoutedEventArgs e)
{
lblResponse.Content = "starting...";
await Task.Run(()=> executeInstall());
lblResponse.Content = "DONE";
}
private void executeInstall()
{
Thread.Sleep(1000); //do time consuming operation
App.Current.Dispatcher.Invoke(() => lblResponse.Content = "Downloading Files...");
Thread.Sleep(1000); //do time consuming operation
App.Current.Dispatcher.Invoke(() => lblResponse.Content = "Unzipping Files...");
Thread.Sleep(1000); //do time consuming operation
App.Current.Dispatcher.Invoke(() => lblResponse.Content = "Updating Files...");
Thread.Sleep(1000); //do time consuming operation
}

Counting how many times an async button has been pressed (or if some operation is going on)

I know that for async operations it is possible to track its progress, but I will try that later. Now I have a simple window forms apply with a button (or a pair of buttons - the number does not matter). The buttons call an async operation
public async void Button1_Click(...)
{
await Button1_OperationAsync(...);
}
If I don't press the button nothing is going on but if I press it once the Button1_OperationAsync starts (and is awaited). (I am not really sure if to call it "a thread").
But what happens if I press the button twice? Well then before the first async operation finishes, the Button1_OperationAsync is called again. (Or if another similar button is pressed then a Button2_OperationAsync is called)
Maybe even the second async operation would finish before the first one.
What I want is a simple way of knowing if any operation is going on. So what I thought is to have a variable and increment it when an operation is called and decrement it when an operation is finished. Something like
int numberOfOps=0;
public async void Button1_Click(...)
{ numberOfOps++;
textBox1.Text="Started!";
await Button1_OpeationAsync(...);
numberOfOps--;
if(numberOfOps<=0)
{
textBox1.Text="Done!";
}
}
Be aware that this code would go in the other button (Button2) too. Or many other buttons.
I am aware that issues of synchronization might be involved. So I would appreciate advice on what I am trying to do in order to do correctly
When using async/await you're not using any threads for the UI code other than the UI-thread. It's possible that the code that gets called in the Button1_OpeationAsync method might use a separate thread, but the calling code will remain on the UI thread.
Try having a play with this code:
private int numberOfOps = 0;
private async void button1_Click(object sender, EventArgs e)
{
textBox1.Text = $"Started! {++numberOfOps}";
await Task.Delay(TimeSpan.FromSeconds(5.0));
textBox1.Text = $"Started! {--numberOfOps}";
if (numberOfOps == 0)
{
textBox1.Text = "Done!";
}
}
It works just fine. You can use the numberOfOps variable across multiple buttons.
If you'd like to make it easy to re-use the code, try it this way:
int numberOfOps = 0;
private async Task RunOp(Func<Task> op)
{
textBox1.Text = $"Started! {++numberOfOps}";
await op();
textBox1.Text = $"Started! {--numberOfOps}";
if (numberOfOps == 0)
{
textBox1.Text = "Done!";
}
}
private async void button1_Click(object sender, EventArgs e)
{
await this.RunOp(() => Button1_OpeationAsync(...));
}
private async void button2_Click(object sender, EventArgs e)
{
await this.RunOp(() => Button2_OpeationAsync(...));
}
Have a task array, and a task object at class level:
private List<Task> tasks = new List<Task>();
private Task task = null;
In each of your click handlers do something like this:
var operationTask = SomeOperationAsync(...);
tasks.Add(operationTask);
task = Task.WhenAll(tasks);
if (task.IsCompleted)
{
// no operation is going on
tasks.Clear();
// do what ever you want to do further
}
else
{
//some operation is going on
}

How to get html source synchronously to a textbox?

I have scanned the tag [cefsharp] and the question I am asking is present in similar forms (please reconsider the duplicate question action).
I am unable to solve what I think should be a simple request. To get the html source of the page in the browser control and interact with winform controls synchronously.
I am weak on the asynchronous side but managed to get the source using a couple of ways I garnered from this tag [cefsharp]. However, any interaction with winform controls triggered from an async event which "locks" in any subsequent method calls to be in a async "mode", and therefore any winform control interaction.
Example 1:
private async void Browser_LoadingStateChanged(object sender,
CefSharp.LoadingStateChangedEventArgs e)
{
if (!e.IsLoading)
{
//-- WORKS, but ...
string html = await Task.Run(Browser.GetSourceAsync);
//-- .. when calling other methods which interact with winform controls,
//-- I have to do so in the following way because I am not on the UI thread.
//-- therefore every control needs a block like this
if (InvokeRequired)
{
this.Invoke(new MethodInvoker(delegate
{
txtSource.Text = html;
}));
}
else
{
txtSource.Text = html;
}
}
}
Example 2:
Another example I have working partly is this method. However, calling from a button click works, while the browser event does not
public string GetSource()
{
string result = null;
if (Browser.IsBrowserInitialized && !Browser.IsDisposed && !Browser.Disposing)
{
Task<string> task = Browser.GetSourceAsync();
var complete = task.ContinueWith(t =>
{
if (!t.IsFaulted)
{
result = t.Result;
}
}, TaskScheduler.Default);
complete.Wait();
}
return result;
}
private void button1_Click(object sender, EventArgs e)
{
//-- WORKS
txtSource.Text = GetSource();
}
private void Browser_LoadingStateChanged(object sender,
CefSharp.LoadingStateChangedEventArgs e)
{
if (!e.IsLoading)
{
//-- DOES NOT WORK. Hangs on waiting for the complete.Wait();
//-- note: this method is not async and neither the button too.
txtSource.Text = GetSource();
}
}

C# in Async Task change Label Text

The following Code does not change the Text and stops executing the Task
private void button1_Click(object sender, EventArgs e)
{
label1.Text = "Test";
Task.Run(() => MyAsyncMethod());
}
public async Task MyAsyncMethod()
{
label1.Text = "";
//everything from here on will not be executed
}
would be really handy if you could use async together with the UI
for accessing a GUI control through a second thread you need to invoke.
following example shows how to set a label's text properly
private void setLabel1TextSafe(string txt)
{
if(label1.InvokeRequired)
label1.Invoke(new Action(() => label1.Text = txt));
else
label1.Text = txt;
}
I hope this solves your problem
would be really handy if you could use async together with the UI
The design of async was carefully done so you can use it naturally with the UI.
in my code i run a function that does a lot of IO and stuff that takes a long time
If you have asynchronous I/O methods (which you should), then you can just do this:
private async void button1_Click(object sender, EventArgs e)
{
label1.Text = "Test";
await MyMethodAsync();
}
public async Task MyMethodAsync()
{
label1.Text = "";
await ...; // "lot of IO and stuff"
label1.Text = "Done";
}
That's the most natural approach.
However, if you need to run code on a background thread (e.g., it's actually CPU-bound, or if you just don't want to make your I/O operations asynchronous like they should be), then you can use IProgress<T>:
private void button1_Click(object sender, EventArgs e)
{
label1.Text = "Test";
var progress = new Progress<string>(update => { label1.Text = update; });
await Task.Run(() => MyMethod(progress));
}
public void MyMethod(IProgress<string> progress)
{
if (progress != null)
progress.Report("");
...; // "lot of IO and stuff"
if (progress != null)
progress.Report("Done");
}
Under no circumstances should modern code use Control.Invoke or (even worse) Control.InvokeRequired.
Task.Run is used to envelope an Action (that is not async) into a Task. Any Task that you want to execute should be awaited. Thus, that Task.Run of yours is rigorously doing nothing.
Mark that button1_Click event handler of yours as async. Then remove that Task.Run and instead do await MyAsyncMethod().
Try this. You don't need to fire a new thread to invoke the async method. compiler will do it for you.
private void button1_Click(object sender, EventArgs e)
{
label1.Text = "Test";
MyAsyncMethod();
}
public async Task MyAsyncMethod()
{
return await Task.Run(() =>{
label1.Text = "";
//everything from here on will not be executed
}
}
I think both the questions and some of the answers are not clear. Depending on where in the task thread you need to update the label you have to use invoke. Otherwise you can leverage await and leverage the standard semantics.
private async void button1_Click(object sender, EventArgs e)
{
label1.Text = "Starting to run a long task... carry on...";
await snooze(3);
label1.Text = "Done with long task";
}
public Task<int> snooze(int seconds)
{
label1.Text = "zzzz...";
return Task.Run(
() => {
label1.Invoke(new Action(() => label1.Text = "For some reason I need to alert you here.. bad dream perhaps...direct access to label1 will fail"));
Thread.Sleep(seconds * 1000);
return seconds;
});
}

check if task with specific method is already running

I have async method OnValueChange where i call static method as a task, my method is updating gui with help of Iprogress intereface and my method is returning Image:
public async void OnValueChange(object sender, EventArgs e)
{
var progress = new Progress<int>(i => ProgresBar.Value = i);
Image im = await Task.Factory.StartNew(() => MyStaticClass.MyStaticFunction(progress),
TaskCreationOptions.LongRunning);
Picture = im;
}
In some cases i am calling OnValueChange function frequently in short period of time, but i want only one task running at time.
what is most efficient method to check if task with specific method is already running?
You should avoid async void. On a side note, I explain on my blog that the way you're using StartNew is dangerous (you should use Task.Run instead).
Applying both of these guidelines to your example, the code will look like this:
public async Task OnValueChangeAsync()
{
var progress = new Progress<int>(i => ProgresBar.Value = i);
Image im = await Task.Run(() => MyStaticClass.MyStaticFunction(progress));
Picture = im;
}
public async void OnValueChange(object sender, EventArgs e)
{
await OnValueChangeAsync();
}
At this point, it becomes more clear how to detect whether a method is already running (since that method actually returns a Task now). One implementation is as such:
private async Task OnValueChangeImplAsync()
{
var progress = new Progress<int>(i => ProgresBar.Value = i);
Image im = await Task.Run(() => MyStaticClass.MyStaticFunction(progress));
Picture = im;
_onValueChangeTask = null;
}
private Task _onValueChangeTask;
public Task OnValueChangeAsync()
{
if (_onValueChangeTask != null)
return _onValueChangeTask;
_onValueChangeTask = OnValueChangeImplAsync();
return _onValueChangeTask;
}
public async void OnValueChange(object sender, EventArgs e)
{
await OnValueChangeAsync();
}

Categories

Resources