Windows form breaks when calling Task.Result - c#

I have code similar to following inside my Windows form:
private async Task<string> GetGreetingAsync()
{
return await Task.Run(() => "Hello");
}
private void button1_Click(object sender, EventArgs e)
{
var x = GetGreetingAsync().Result;
}
Clicking the button causes the entire Windows form to freeze and become unresponsive. No exception is thrown.
Even when I don't use the task .Result directly in the event handler and the whole async marked code is in some class library function, which provides interface without async, the problem still occurs. Unit tests for such class library pass without any problems, but when I call its function from event handler on Windows form, it breaks and does not respond.
Why does this happen? How can I fix it?

You are blocking the the UI thread with .Result; (see ConfigureAwait)
private async Task<string> GetGreetingAsync()
{
return await Task.Run(() => "Hello").ConfigureAwait(false);
}
private void button1_Click(object sender, EventArgs e)
{
var x = GetGreetingAsync().Result;
}
Or go all the way async
private async Task<string> GetGreetingAsync()
{
return await Task.Run(() => "Hello");
}
async private void button1_Click(object sender, EventArgs e)
{
var x = await GetGreetingAsync();
}
Using this version you don't even need to await in GetGreetingAsync
private Task<string> GetGreetingAsync()
{
return Task.Run(() => "Hello");
}
async private void button1_Click(object sender, EventArgs e)
{
var x = await GetGreetingAsync();
}

You should be using await instead of Result on a Task<T> to get non-blocking behavior.
private async void button1_Click(object sender, EventArgs e)
{
var x = await GetGreetingAsync();
}
Calling Result causes the code to wait until the Task has completed where calling await allows the async behavior to be used.
Edit
Actually looking at your code again if you're doing
private async Task<string> GetGreetingAsync()
{
return await new Task<string>(() => "Hello");
}
That is really not needed. That block can just be
private Task<string> GetGreetingAsync()
{
return new Task<string>(() => "Hello");
}
There's no need for the thing returning the task to be async.

Related

async function running in sequence WPF

so I am making a small WPF app.
I am new to C# and Multithreading, I want to run certain methods in sequence but because one of the methods is Async it does not run in sequence.
private async void LoadButton_Click(object sender, RoutedEventArgs e)
{
if (!OpenFile()) return; // opens a file dialog and ensure format is correct
await Task.Run(() =>
{
// some heavy task which I run here so that I dont freeze the UI
});
}
private void TheFunctionIwantToRunInSeqeuence(object sender, RoutedEventArgs e)
{
LoadButton_Click(sender, e);
SaveCareerInfoButton_Click(sender, e); // I want this line to wait for load to finish
LoadButton_Click(sender, e);
ImportCareerInfoButton_Click(sender, e); // I want this line to wait for the second load to finish
}
Await these calls as well, refactor your code a bit.. extract handler's content to a separate method and don't pass senders and args between handlers
private Task Load()
{
if (!OpenFile()) return;
return Task.Run(() =>
{
// some heavy task which I run here so that I dont freeze the UI
});
}
private async void LoadButton_Click(object sender, RoutedEventArgs e)
{
await Load();
}
private async void TheFunctionIwantToRunInSeqeuence(object sender, RoutedEventArgs e)
{
await Load();
// refactor your code to not pass sender, e.. SaveCareer();
SaveCareerInfoButton_Click(sender, e);
await Load();
// refactor your code to not pass sender, e.. ImportCareer();
ImportCareerInfoButton_Click(sender, e);
}
You can use await to wait an async function to finish and make function TheFunctionIwantToRunInSeqeuence to async Task return type:
private async Task TheFunctionIwantToRunInSeqeuence(object sender, RoutedEventArgs e)
{
await LoadButton_Click(sender, e);
SaveCareerInfoButton_Click(sender, e); // I want this line to wait for load to finish
await LoadButton_Click(sender, e);
ImportCareerInfoButton_Click(sender, e); // I want this line to wait for the second load to finish
}

Cancel button click event while executing heavy method in C#

I want to cancel button click event while executing any heavy method in C#.
Lets say there is following method and event:
private void Submit()
{
//This method requires 5 seconds and it is blocking method
}
private void button_Click(object sender, EventArgs e)
{
//Some code here
}
When I click on button while executing Submit method, the button click event does not fire, instead of it fires after completed the execution of Submit method.
I want to cancel this event when clicked while executing the Submit method.
I can not disable this button as my application contains many of such buttons.
I also want to prevent all events to be fired that is initiated during execution of Submit method if possible and allow all events to be fired after execution of Submit method.
Pls suggest any solution or workaround to achieve the about task.
If you start a new task using async, you will be able to call the cancel method whilst executing the Submit.
private async Task Submit()
{
await Task.Factory.StartNew(() =>
{
// Blocking code, network requests...
});
}
Have a look at https://msdn.microsoft.com/en-us/library/mt674882.aspx for await async reference, and also Channel 5 has some great videos about it https://channel9.msdn.com/Series/Three-Essential-Tips-for-Async
If you want to stop "Submit" whenever you trigger "Cancel", I would recommend using a CancellationEventSource to cancel the task, have a look:
using System.Threading.Tasks;
private CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
private async Task Submit()
{
var cancellationToken = this.cancellationTokenSource.Token;
await Task.Factory.StartNew(() =>
{
cancellationToken .ThrowIfCancellationRequested();
// Blocking code, network requests...
}, this.cancellationTokenSource.Token);
}
private void button_Click(object sender, EventArgs e)
{
this.cancellationTokenSource.Cancel();
}
Have a look at this msdn article on CancellationTokenSource and Task https://msdn.microsoft.com/en-us/library/dd997396(v=vs.110).aspx
Edit 1:
I don't want to stop the Submit() any more. I want to cancel the event
during execution of Submit() and allow event to be fired after
completion of the Submit()
Create a Task and await for it to finish:
private Task submitTask;
private void Submit()
{
this.submitTask = Task.Factory.StartNew(() =>
{
// Some Code
}
}
private async void button_Click(object sender, EventArgs e)
{
// Awaiting will prevent blocking your UI
await this.submitTask;
// Some Code
}
You can use Task to achieve that.
CancellationTokenSource _cancellationTokenSource = new CancellationTokenSource();
CancellationToken _ct;
private void Submit()
{
_ct = cancellationTokenSource.Token;
var task = Task.Factory.StartNew(() =>
{
_ct.ThrowIfCancellationRequested();
//your code here
}, _ct);
}
private void button_Click(object sender, EventArgs e)
{
//Some code here
_cancellationTokenSource.Cancel();
}
CancellationToken is an object that allows you to send to 'Task' information that the task should be cancelled. Additionally inside your Task make sure you are ThrowIfCancellationRequested to make sure that this task will be stopped.
In my example button_click is the cancel button.
More information you can possibly find here: https://msdn.microsoft.com/en-us/library/dd997396(v=vs.110).aspx
delegate void submitdelegate();
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
IAsyncResult ia;
private void Form1_Load(object sender, EventArgs e)
{
submitdelegate sd=Submit;
ia=sd.BeginInvoke(()=>{},null,null);
}
private void Submit()
{
}
void button_Click(object sender, EventArgs e)
{
if(ia.IsCompleted)
{//run all the things you want here
}
}
creating an instance in Iasyncresult will give you wether the delegate finished executioin.
http://www.dotnetcurry.com/ShowArticle.aspx?ID=634
Your goal is to prevent the button(s) from having any effect if a call to Submit() is currently active.
To do so:
(1) Wrap the call to Submit() inside an async method that calls Submit() as a separate task:
private async Task SubmitAsync()
{
await Task.Run(() => Submit());
}
(2) Add to the class a boolean field that will be used to check if Submit() is currently executing:
private bool submitIsActive = false;
(3) Change your button click handler(s) to be async and check the submitIsActive flag before awaiting SubmitAsync():
private async void button_Click(object sender, EventArgs e)
{
if (!submitIsActive)
{
submitIsActive = true;
await SubmitAsync();
submitIsActive = false;
}
}
At first glance you might think that there's a race condition where submitIsActive is set to true in one thread, but checked in a different thread. This is not so because the button_Click() method will only ever be called from the UI thread.
Also note that this approach does NOT allow you to cancel a call to Submit() - but I don't think you were asking for that.
What about unsubscribing the buttons from their click events for the duration of the Submit() method? Too many buttons?

Stop Task when task run [duplicate]

This question already has an answer here:
Async/await for long-running API methods with progress/cancelation
(1 answer)
Closed 6 years ago.
How can i totally stop a task when task running?
private async void button1_Click(object sender, EventArgs e)
{
await Backup(file);
}
public async Task Backup(string File)
{
await Task.Run(() =>
{
1)do something here
2)do something here
3)do something here
});
}
private async void button2_Click(object sender, EventArgs e)
{
<stop backup>
}
If say i want to stop task during 2nd thing is processing, and i click a button2 then the task will stop process
How do I cancel or end the task from button2_Click?
// Define the cancellation token source & token as global objects
CancellationTokenSource source = new CancellationTokenSource();
CancellationToken token;
//when button is clicked, call method to run task & include the cancellation token
private async void button1_Click(object sender, EventArgs e)
{
token = source.Token;
await Backup(file, token);
}
public async Task Backup(string File, CancellationToken token)
{
Task t1 = Task.Run(() =>
{
//do something here
},
token);
}
//cancel button click event handler
private async void cancelButton_Click(object sender, EventArgs e)
{
if(source != null)
{
source.Cancel();
}
}
//tasks
https://msdn.microsoft.com/en-us/library/system.threading.tasks.task(v=vs.110).aspx
//CancellationToken
https://msdn.microsoft.com/en-us/library/system.threading.cancellationtoken(v=vs.110).aspx

Convert method to return a task

How can I turn the below method into the right format for returning a task items. The method stub requires a task to be used. This is my first time into async methods I am using it for the windows phone 8 like below:
private System.Threading.Tasks.Task listView_PullToRefreshRequested(object sender, EventArgs e)
{
Populatelist();
}
public async void Populatelist()
{
try
{
curoListsDal _db = new curoListsDal();
cLists = await _db.GetListsAync();
listView.ItemsSource = cLists;
}
catch (Exception ex)
{
}
}
The right format would be to return a Task instead of void in PopulateListAsync and await on that inside your event handler:
private async void PullToRefreshRequestAsync(object sender, EventArgs e)
{
await PopulateListAsync();
}
public async Task PopulateListAsync()
{
curoListsDal db = new curoListsDal();
listView.ItemsSource = await db.GetListsAsync();
}
Side note: don't swallow exceptions.

How can i make this method async?

I was wondering how i could make this method async, it gives me an error: The 'await' operator can only be used within an async method. Consider marking this method with the 'async' modifier and changing its return type to 'Task'.
Here's my code:
void OnRegister(object sender, RoutedEventArgs e)
{
await PrintManager.ShowPrintUIAsync();
}
You're welcome:
async void OnRegister(object sender, RoutedEventArgs e)
{
await PrintManager.ShowPrintUIAsync();
}
You can't use await without a async method(this would not make any sense).
async Task<int> OnRegisterAsync(object sender, RoutedEventArgs e)
{
await PrintManager.ShowPrintUIAsync();
return 0;
}
You need to decorate the method which contains 'await' with 'async'.
In your case I'm guessing you can't make OnRegister return a Task so you'll need a helper method. Something like this should work:
void OnRegister(object sender, RoutedEventArgs e)
{
RegisterAsync();
}
async void RegisterAsync() {
await PrintManager.ShowPrintUIAsync();
}
Notice how the RegisterAsync method is decorated with 'async' and returns a Task. Which in turn needs to be started by the caller.
Reference: http://msdn.microsoft.com/en-us/library/hh191443.aspx

Categories

Resources