This is my implementation of async/await in Windows Forms Application
async Task<int> DoAysnc1()
{
await Task.Delay(3000);
return 3000;
}
async Task<int> DoAsync2()
{
await Task.Delay(5000);
return 5000;
}
private async void button1_Click(object sender, EventArgs e)
{
this.textBox1.Text = "";
var doAsync1 = DoAysnc1();
var doAsync2 = DoAysnc2();
var async1 = await doAsync1;
var async2 = await doAsync2;
this.textBox1.Text = $"{async1} & {async2}";
}
After 5 seconds the result in the TextBox is "3000 & 5000".
But when I modify button1_Click like this:
private async void button1_Click(object sender, EventArgs e)
{
this.textBox1.Text = "";
var async1 = await DoAysnc1();
var async2 = await DoAysnc2();
this.textBox1.Text = $"{async1} & {async2}";
}
the result is the same but it takes 8 sec.
Why second version of button1_Click act as synchronous?
Below is the explanation of the difference:
this.textBox1.Text = "";
var doAsync1 = DoAysnc1(); // <--- Run DoAsync1
var doAsync2 = DoAysnc2(); // <--- Run DoAsync2
var async1 = await doAsync1; // <--- wait for DoAsync1 to finish
var async2 = await doAsync2; //<--- wait for DoAsync2 to finish
this.textBox1.Text = $"{async1} & {async2}";
While:
this.textBox1.Text = "";
var async1 = await DoAysnc1(); // <-- Run DoAsync1 and wait for it to be done
var async2 = await DoAysnc2(); // <-- Run DoAsync2 and wait for it to be done
this.textBox1.Text = $"{async1} & {async2}";
So in the first version, both tasks are running at the same time. While in the second version you never run the second task until the first is Completed.
I think reading this Article is going to be a big plus for your knowledge.
With some comments to make what actually happens more clear:
// Starts DoAsync1 asynchronously
var doAsync1 = DoAysnc1();
// Starts DoAsync2 asynchronously
var doAsync2 = DoAysnc2();
// From now on, both task are executing
// Wait for DoAsync1 completion
var async1 = await doAsync1;
// Wait for DoAsync2 completion
var async2 = await doAsync2;
For the second case:
// Starts DoAsync1 asynchronously, and wait for the task completion (3s)
var async1 = await DoAysnc1();
// Starts DoAsync2 asynchronously, and wait for the task completion (5s)
var async2 = await DoAysnc2();
DoAsync1 and DoAsync2 return awaitable Task-objects. If you await them one by one then they are executed one after another (first wait for the first one to finish, then for the second one). In order to run them in parallel you can simply create the Task-objects first and wait for their results with Task.WhenAll.
private async void button1_Click(object sender, EventArgs e)
{
this.textBox1.Text = "";
var task1 = DoAysnc1();
var task2 = DoAysnc2();
await Task.WhenAll(task1, task2)
this.textBox1.Text = $"{task1.Result} & {task2.Result}";
}
Related
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.
}
With the await of MapLocationFinder my program still runs, even after trying to close it with Application.Current.Shutdown();. I'm a beginer.
I already tried to use CancellationToken or run this as Task. But I don't know if I had done this in the right way. I tried different thinks for some hours but nothing worked for me.
private async Task GetLocation()
{
var accesStatus = await Geolocator.RequestAccessAsync();
switch (accesStatus)
{
case GeolocationAccessStatus.Allowed:
// locate user
var locator = new Windows.Devices.Geolocation.Geolocator();
var location = await locator.GetGeopositionAsync();
var position = location.Coordinate.Point.Position;
// get city name
Geopoint geopoint = new Geopoint(new BasicGeoposition
{
Latitude = position.Latitude,
Longitude = position.Longitude
});
Here the problem starts
MapLocationFinderResult result = await MapLocationFinder.FindLocationsAtAsync(geopoint, MapLocationDesiredAccuracy.Low);
if (result.Status == MapLocationFinderStatus.Success)
{
locationBlock.Text = "City: " + result.Locations[0].Address.Town;
}
problem ended, the rest is just for the context
// calculate time
int[] sun = SunDate.CalculateSunriseSunset(51.434406, 6.762329);
var sunrise = new DateTime(1, 1, 1, sun[0] / 60, sun[0] - (sun[0] / 60) * 60, 0);
var sunset = new DateTime(1, 1, 1, sun[1] / 60, sun[1] - (sun[1] / 60) * 60, 0);
//fit UI
lightStartBox.Text = sunrise.Hour.ToString();
darkStartBox.Text = sunset.Hour.ToString();
// apply settings
lightStartBox.IsEnabled = false;
darkStartBox.IsEnabled = false;
break;
case GeolocationAccessStatus.Denied:
locationCheckBox.IsChecked = false;
locationBlock.Text = "The App needs permission to location";
await Windows.System.Launcher.LaunchUriAsync(new Uri("ms-settings:privacy-location"));
break;
case GeolocationAccessStatus.Unspecified:
locationCheckBox.IsChecked = false;
locationBlock.Text = "The App needs permission to location";
await Windows.System.Launcher.LaunchUriAsync(new Uri("ms-settings:privacy-location"));
break;
}
return;
}
If I close the program, it should also end the await task. Better: It should end the operation after he got the info.
If I close the program, it should also end the await task. Better: It should end the operation after he got the info.
I have run your code, but I could not reproduce the issue, I could get MapLocationFinderResult with low delay. I found you used MapLocationDesiredAccuracy.Low parameter. And it will leverage the maps disk cache to get accurate info up to the city level. maps disk cache may cause this issue. You could try to use MapLocationDesiredAccuracy.High parameter.
As you see, FindLocationsAtAsync is IAsyncOperation method. So, you could cancel it manually or set timeout cancel token.
For example
private IAsyncOperation<string> GetAsyncOperation()
{
return AsyncInfo.Run<string>(
(token) => // CancellationToken token
Task.Run<string>(
() =>
{
token.WaitHandle.WaitOne(3000);
token.ThrowIfCancellationRequested();
return "hello";
},
token));
}
private IAsyncOperation<string> operation;
private async void Button_Click(object sender, RoutedEventArgs e)
{
try
{
operation = GetAsyncOperation();
var res = await operation;
}
catch (Exception)
{
System.Diagnostics.Debug.WriteLine("method end");
}
}
private void Cancel_Button_Click(object sender, RoutedEventArgs e)
{
operation?.Cancel();
}
Set Timeout
private async void Button_Click(object sender, RoutedEventArgs e)
{
var source = new CancellationTokenSource(4000);
var res = await GetAsyncOperation().AsTask(source.Token);
}
Looks like this is a known bug
To work-around it I ended up setting a static flag on my App class so that when the app was shutting down it would force kill the process.
// Before call to MapLocationFinder.FindLocationsAsync()
App.RequiresProcessKill = true;
and then in my shutdown process (ie in the OnClosed method of your main window) I forced closed the app if neccessary:
protected override void OnClosed(EventArgs e)
{
base.OnClosed(e);
if (App.RequiresProcessKill)
{
var self = Process.GetCurrentProcess();
self.Kill();
}
}
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");
}
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 want to show in textbox the time execution of a function that is called using Task.Run, since takes some time
to complete and I'd created a thread for that.
The issue is when I click on begin button is printed inmediately the time in textBox1 and I want to show the
Elapsed time but only just after MyFunction completes the processing or when Cancel button is pressed.
Where should go sw.Stop()?
My current code for Begin and cancel button is:
void Begin_Click(object sender, EventArgs e)
{
Stopwatch sw = Stopwatch.StartNew();
// Pass the token to the cancelable operation.
cts = new CancellationTokenSource();
Task.Run(() => MyFunction(inputstring, cts.Token), cts.Token);
sw.Stop();
textBox1.Text += Math.Round(sw.Elapsed.TotalMilliseconds / 1000, 4) + " sec";
}
void Cancel_Click(object sender, EventArgs e)
{
if (cts != null)
{
cts.Cancel();
cts = null;
}
}
You're not waiting for the MyFunction to complete, You're simply calculating the start time of the Task.Run call. To wait for the MyFunction to complete, you can await the Task returned by Task.Run.
async void Begin_Click(object sender, EventArgs e)//<--Note the async keyword here
{
Stopwatch sw = Stopwatch.StartNew();
// Pass the token to the cancelable operation.
cts = new CancellationTokenSource();
await Task.Run(() => MyFunction(inputstring, cts.Token), cts.Token);//<--Note the await keyword here
sw.Stop();
textBox1.Text += Math.Round(sw.Elapsed.TotalMilliseconds / 1000, 4) + " sec";
}
If you're new to asynchronous programming start by reading here and here