SemaphoreSlim and async/await - c#

This works:
int _counter;
readonly SemaphoreSlim _semaphore = new SemaphoreSlim(1, 1);
async void Button_Click(object sender, RoutedEventArgs e)
{
if (_semaphore.Wait(0))
{
Title = $"{ ++_counter}";
await Task.Delay(1000); // simulate work
Title = $"{ --_counter}";
_semaphore.Release();
}
}
After first click further buttons clicks are ignored until work is finished. Tittle can be 1 or 0.
And this doesn't work
void Button_Click(object sender, RoutedEventArgs e)
{
if (_semaphore.Wait(0))
{
Test(); // moving code into separate method
_semaphore.Release();
}
}
async void Test()
{
Title = $"{ ++_counter}";
await Task.Delay(1000); // simulate work
Title = $"{ --_counter}";
}
Clicking button continuously will rise Tittle to 2, 3, and so on.
What am I doing wrong?

async void only makes sense in event handlers. It is a special type of asynchronism. It means "fire and forget".
You don't want to fire and forget the Test() method. You want to wait for it to return.
Change your Test signature to:
// Note that it returns a "Task". A task is an awaitable promise.
async Task Test()
{
//...
}
And then await it on your event handler:
async void Button_Click(object sender, RoutedEventArgs e)
{
if (_semaphore.Wait(0))
{
await Test(); // moving code into separate method
_semaphore.Release();
}
}

Related

Cancel task in c#

I´m still learning to code, and I'm making a new project with MDI forms (C# and Visual Studio 2019). In mdichild, I launched a task, but if the form is unloaded, the task still remains. I would like to know how cancel the task, even on a cancel button click.
The code:
private async void BuscaActualizaciones()
{
await Task.Run(() => DoLongThing());
}
private void DoLongThing()
{
//some hard stuff
}
private void BtnBuscar_Click(object sender, EventArgs e)
{
//In here i launch the task with hard stuff
BuscaActualizaciones();
}
This code works perfectly, but I need to cancel in some events, and I don't know how.
I tried some home-made tricks, and read on Google about task cancellation, but all of them used Task in other way that I don't understand. I'm still learning, and it is my first time with tasks.
As others have pointed out, the correct solution will use a CancellationToken. However, you don't want to pass it to Task.Run; you want to pass it to DoLongThing, as such:
CancellationTokenSource cts = new ();
private async void BuscaActualizaciones()
{
await Task.Run(() => DoLongThing(cts.Token));
}
private void DoLongThing(CancellationToken token)
{
...
token.ThrowIfCancellationRequested();
}
private void CancelButton_Click(object sender, EventArgs e)
{
cts.Cancel();
}
Polling for cancellation (ThrowIfCancellationRequested) is common for CPU-bound methods that need to periodically check if they're canceled. I have a blog post that goes into more details on the subject.
As #freakish pointed out in the comment, the proper way to do this is using a CancellationToken. Here is a simple example, assuming you have button which cancels the task when clicked:
CancellationTokenSource cts = new();
private async Task BuscaActualizaciones()
{
await Task.Run(DoLongThing, cts.Token);
}
private Task DoLongThing() // <-- needs to return a Task
{
//some hard stuff
cts.Token.ThrowIfCancellationRequested();
//some hard stuff
}
private void CancelButton_Click(object sender, EventArgs e)
{
cts.Cancel(); // <-- here is the cancallation
}
I also strongly recommend recommend the documentation regarding "Asynchronous programming with async and await".
CancellationTokenSource cts = new CancellationTokenSource ();
private async void BuscaActualizaciones()
{
await Task.Run(() => DoLongThing(), cts.Token);
}
private Void DoLongThing()
{
//some hard stuff
if (cts.IsCancellationRequested)
{
throw new TaskCanceledException();
}
}
private void CancelButton_Click(object sender, EventArgs e)
{
cts.Cancel(); // <-- here is the cancellation
}
It´s working perfectly. thank you to all with help. The cts.IsCancellationRequested I read in google. Thank you .

C# Task completion callback an exceptions

I have a WindowsForms application that I would like to be able to leverage async Task. There are a couple of issues that I'm having:
How do you handle ContinueWith so you can run continuation in the form?
What if the Form can be closed before the Task completes?
private void buttonGo_Click(object sender, EventArgs e)
{
val t = RunSomethingAsync().ContinueWith( p => { OnRunDone(); });
}
private void OnRunDone()
{
Invoke( p => { button.Enabled = true; });
}
Is the above code the right way to do this?
What happens in the above code if the Form is closed before the Task completes?
What if the RunSomethingAsync throws an uncaught exception?
I think I figured it out. I just didn't quite understand how await/async works. The solution is:
private async void buttonGo_Click(object sender, EventArgs e)
{
await RunSomethingAsync();
OnTaskComplete();
}
private void OnTaskComplete()
{
label1.Text = "Done";
}
private async Task RunSomethingAsync()
{
await Task.Delay(1000);
}

Implement asnyc method inside a Thread

I'm working on Xamarin Forms project in Visual Studio 2017 . I need to implement async method inside thread .
Event which starting the thread
public void btnAction_Click(object sender, System.EventArgs e)
{
var load = new System.Threading.Thread((t) =>
{
ShowWarning();
});
load.Start(btnText);
}
async method should implement inside the thread
private async void ShowWarning()
{
bool response = await DisplayAlert("Warning", "Please Enter
The Key","Yes","No");
}
If DisplayAlert is truely asynchronous, why do you start a thread at all? You can declare your event handler async and simply awiat ShowWarning:
public async void btnAction_Click(object sender, System.EventArgs e)
{
await ShowWarning();
}
You might want to disable btnAction before the await and re-enable it after the await again, so you avoid another click event while awaiting.
And note that it's bad practice to declare ShowWarning as async void. It should be
private async Task ShowWarning()
{ ... }
(In case of the button event handler it's ok to return async void because otherwise you couldn't assign it to an event).
If you really need to run ShowWarning on a different thread, you can use Task.Run():
public async void btnAction_Click(object sender, System.EventArgs e)
{
await Task.Run(ShowWarning);
}

Asynchronous Displaying in Windows Form Application

I don't think the following question is rarely seen. But since I don't know how to search for the right answer, so I'm still stuck on it.
I have a label in the form and I want to show something before another job is invoked:
private void btnLoadEvent_Click(object sender, EventArgs e)
{
lbStatus.Text = "Loading... ";
load();
}
private void load()
{
Thread.Sleep(5000);
lbStatus.Text = "Done";
}
The label won't show anything before load() is complete.
Then I changed my program usnig async/await:
private async void btnLoadEvent_Click(object sender, EventArgs e)
{
lbStatus.Text = "Loading... ";
await load();
}
private async Task load()
{
Thread.Sleep(5000);
lbStatus.Text = "Done";
}
It doesn't change anything. Does anyone have any idea?
2014/4/3:
As a note, I think I've figured out how async/await works. And to avoid misleading any possible reader, I list my final solution as follows:
private async void btnLoadEvent_Click(object sender, EventArgs e)
{
lbStatus.Text = "Loading... ";
string content = await loadAsync();
}
private async Task<string> loadAsync()
{
using (WebClient webClient = new WebClient())
{
string json = await webClient.DownloadStringTaskAsync(
"http://api.openweathermap.org/data/2.5/weather?q=Taipei,tw");
lbStatus.Text = "Done";
return json;
}
}
In practice, to meet the Decoupling Principle, "lbStatus.Text = ... " could be better moved from loadAsync() to btnLoadEvent_Click() although it works fine.
And thanks everyone who helped.
You didn't really go async. Thread.Sleep is blocking and because your async function never yields it will still block the call site. Use await Task.Delay(... instead.
private void btnLoadEvent_Click(object sender, EventArgs e)
{
lbStatus.Text = "Loading... ";
load();
}
private async Task load()
{
await Task.Delay(5000);
lbStatus.Text = "Done";
}
Try using a BackgroundWorker . Set your label.Text to Loading before executing the BackgroundWorker's RunWorkerAsync method. Do your stuff (like your Thread.Sleep(5000)) in the Do_Work event and change the status of your label in the RunWorkerCompleted event. Visit this link to see an example.
You need to use BackgroundWorker. Something like that.
private void button1_Click(object sender, EventArgs e)
{
label1.Text = "Loading";
backgroundWorker1.RunWorkerAsync();
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
load();
}
private void load()
{
System.Threading.Thread.Sleep(1000);
if(InvokeRequired)
this.Invoke(new Action(()=>label1.Text = "Done"));
}

My window doesnt remain active after i click on "async" method button

i have the following code:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void Button_Click_1(object sender, RoutedEventArgs e)
{
this.Background = Brushes.Red;
}
private void Button_Click_2(object sender, RoutedEventArgs e)
{
int res = WaitFiveSecMethod();
this.text2.Text = res.ToString();
}
private int WaitFiveSecMethod()
{
Thread.Sleep(5000);
return 5;
}
private async void Button_Click_3(object sender, RoutedEventArgs e)
{
var res = await WaitFiveSecMethodAsync();
this.text1.Text = res;
}
private async Task<string> WaitFiveSecMethodAsync()
{
Thread.Sleep(5000);
return "5";
}
}
I want to show the difference between clicking the "sync" button (button 2) and clicking the "async" button (button 3). clicking both buttons should do the same time-consuming calculations (in my case represented with "Thread.Sleep(5000)").
Currently using async and wait make my client stuck i thought these keywords shouldn't block my client thread.
what am i doing wrong?
Your code below works synchronously and block UI, not async:
private async Task<string> WaitFiveSecMethodAsync()
{
Thread.Sleep(5000);
return "5";
}
Remember async has just responsibility to enable await keyword, if you don't have await, async is meaningless and turn to synchronous. So, async method must have at least one await keyword inside.
The better way is, you should delay by awaiting Task.Delay which does not block your UI thread:
private async Task<string> WaitFiveSecMethodAsync()
{
await Task.Delay(5000);
return "5";
}

Categories