I have a List of Task<bool> that i want to iterate it and depending on the awaited result i decide whether to continue or break but ironically the foreach just executes the tasks and await keyword does not work
Here is my code
private async void Execute(object sender, EventArgs e)
{
var tList = new List<Task<bool>> { Method1(), Method2()};
foreach (var task in tList)
{
var result = await task;
if(!result)
break;
}
}
public async Task<bool> Method1()
{
await Task.Delay(1000);
Console.WriteLine("Method1");
return false;
}
public async Task<bool> Method2()
{
await Task.Delay(1000);
Console.WriteLine("Method2");
return true;
}
Result : Both functions are execute.
Question : How could i use await inside foreach ?.
And thanks in advance.
You can use await within a foreach exactly as you are doing now.
Result : Both functions are execute.
Both functions should execute. The result isn't set to false until Method2 returns, at which point both have already run. You're also starting both Task<bool> instances before you await either, so both are (potentially) running before your foreach loop.
Reverse the order of your methods, and they won't both necessarily run (though they may, as you're starting them):
var tList = new List<Task<bool>> { Method2(), Method1()};
If you want to delay this completely, you could write it as:
var tList = new List<Func<Task<bool>>> { Method2, Method1};
foreach (var taskFunc in tList)
{
var result = await taskFunc();
if(!result)
break;
}
Related
So, I use the Dropbox API in my C# application to download files and check strings. Previously, I've always used the "DownloadAsync" method to download a file and get it's contents in an "async void" method. Recently, however, I also needed to return a boolean, so I put the same code that I've always used in an "async Task" method, so that I could check the result. However, when I do this, the "DownloadAsync" method never finishes (runs forever). If I take the same method and put it in an "async void" method (without returning a result) the "DownloadAsync" method finishes just fine. I'm not sure why this makes a difference at all, but it does.
In this first method, the "keysToCheck" string gets set, boolean gets set, and method finishes.
private async void SetStringAndBoolean(string text)
{
var keysToCheck= "";
bool booleanToCheck;
using (var dbx = new DropboxClient("TOKEN"))
{
using (var response = await dbx.Files.DownloadAsync("/FileToDownload.txt"))
{
keysToCheck = await response.GetContentAsStringAsync();
}
}
var keys = keysToCheck.Split(new[] { Environment.NewLine }, StringSplitOptions.None).ToList();
foreach (var key in keys)
if (key == text)
booleanToCheck = true;
booleanToCheck = false;
}
However, in the following method, the "DownloadAsync" method never finishes. It just runs forever. Any ideas what I'm doing wrong?
private async Task<bool> SetStringAndReturnBoolean(string text)
{
var keysToCheck = "";
using (var dbx = new DropboxClient("TOKEN"))
{
using (var response = await dbx.Files.DownloadAsync("/FileToDownload.txt"))
{
keysToCheck = await response.GetContentAsStringAsync();
}
}
var keys = keysToCheck.Split(new[] { Environment.NewLine }, StringSplitOptions.None).ToList();
foreach (var key in keys)
if (key == text)
return true;
return false;
}
Calling Method (Button Click Event):
private async void Button_Click(object sender, RoutedEventArgs e)
{
if (!SetStringAndReturnBoolean(GuiTextBox.Text).Result)
ErrorRun.Text = "Invalid Key";
else
{
DialogResult = true;
Close();
}
}
Use await to call the method instead of accessing Result of the returned Task.
private async void Button_Click(object sender, RoutedEventArgs e)
{
bool result = await SetStringAndReturnBoolean(GuiTextBox.Text);
if (!result)
{
ErrorRun.Text = "Invalid Key";
}
else
{
DialogResult = true;
Close();
}
}
Async only works when you use it consistently. Never mix await and Task.Result or Task.Wait() unless you know very well what you do.
I have the following pieces of code:
private async void buttonStart_ItemClick(object sender, DevExpress.XtraBars.ItemClickEventArgs e)
{
Bot b = new Bot(_names);
var result = await b.Start(false, true, false);
MessageBox.Show("Done");
}
public async Task<bool> Start(bool instagram, bool twitter, bool xbox)
{
if (twitter)
{
Twitter t = new Twitter(Names);
await t.CheckNames();
}
return true;
}
public Task CheckNames()
{
List<Task> tasks = new List<Task>();
foreach (Name name in Names)
{
tasks.Add(Task.Factory.StartNew(async () =>
{
TwitterResponse result = await Check(name);
MessageBox.Show(result.msg);
}));
}
return Task.WhenAll(tasks);
}
public async Task<TwitterResponse> Check(Name name)
{
HttpClient http = new HttpClient();
HttpResponseMessage response = await http.GetAsync(string.Format("https://twitter.com/users/username_available?username={0}", name.Value));
string html = response.Content.ReadAsStringAsync().Result;
TwitterResponse result = new JavaScriptSerializer().Deserialize<TwitterResponse>(html);
return result;
}
However, I always seem to get the MessageBox saying "Done" before any of the tasks are completed.
Am I doing something wrong, how can I make sure all of the tasks actually complete before getting the messagebox?
The problem is the line tasks.Add(Task.Factory.StartNew(async () =>, you should almost never be using Task.Factory.StartNew and instead use Task.Run(.
The object that StartNew is returning is a Task<Task> which means it does not wait for the inner task to finish. You must either call .Unwrap() on the output of the StartNew before you add it to the collection or, much much better use Task.Run(
tasks.Add(Task.Run(async () =>
{
TwitterResponse result = await Check(name);
MessageBox.Show(result.msg);
}));
Which has a overload that takes in a Func<Task> and will unwrap the inner task for you.
How do I wrap code like below in a Task based async method?
void ExecuteThreadedAsync(Action a) {
ThreadPool.QueueUserWorkItem(x=>
{
action();
});
}
Currently, this method is called like:
void Method() {
var context = GetSomeContext();
ExecuteThreadedAsync(() =>
{
var result = TimeConsumingWebServiceCall();
context.Result = result;
});
}
But what I want is something like:
async void Method() {
var context = GetSomeContext();
await ExecuteTaskBasedAsync(() =>
{
var result = TimeConsumingWebServiceCall();
context.Result = result;
});
}
Or:
async void Method() {
var context = GetSomeContext();
var result = await ExecuteTaskBasedAsync<Result>(() =>
{
var result = TimeConsumingWebServiceCall();
return result;
});
context.Result = result;
}
It depends on what the method TimeConsumingWebServiceCall() does - from its name, I infer that it calls a web service and time consuming happens due to slow response from the service. This is a IO bound task. Using ThreadPool.QueueUserWorkItem or Task.Run to synchronously call IO bound task is generally an anti-pattern. You are just offloading work to another thread which is anyway going to synchronously block on the web service call.
Task.Run is more suitable to offload compute bound tasks.
In your case, you should investigate how the TimeConsumingWebServiceCall actually calls the web service and you should use asynchronous IO API's.
Are you using WCF? Choose to Generate task-based operations while creating proxy (Add Service Reference).
Are you using System.Net.WebClient? Switch to the new System.Net.Http.HttpClient - start using its asynchronous methods like GetStringAsync or GetStreamAsync.
This way you can actually leverage the benefit of asynchronous API without un-necessarily blocking threads.
async Task Method() {
var context = GetSomeContext();
var result = await TimeConsumingWebServiceCallAsync();
context.Result = result;
}
async Task TimeConsumingWebServiceCallAsync() {
var httpClient = new HttpClient();
var results = await httpClient.GetStringAsync(url); // or await wcfProxy.YourWCFMethodAsync();
// do processing if necessary
return results;
}
You can use the static Task.Run method:
async void Method() {
var context = GetSomeContext();
await Task.Run(() =>
{
var result = TimeConsumingWebServiceCall();
context.Result = result;
});
}
or
async void Method() {
var context = GetSomeContext();
context.Result = await Task.Run(() => TimeConsumingWebServiceCall());
}
This is what you can do in order to wrap a TimeConsumingWebServiceCall so that it can be executed asynchronously
async void MethodAsync()
{
var context = GetSomeContext();
context.Result = await Task.Factory.StartNew(() =>
{
return TimeConsumingWebServiceCall();
});
}
A bit more advanced approach
async void Method()
{
var TimeConsumingTask = Task.Factory.StartNew(() =>
{
return TimeConsumingWebServiceCall();
});
var context = GetSomeContext();
context.Result = await TimeConsumingTask;
}
The reason behind this is to start the TimeConsumingTask even before you invoke GetSomeContext() as it can be executed parallely to TimeConsumingTask.
When GetSomeContext() finishes you await for the TimeConsumingTask to finish to assign it's result.
I just can't seem to understand how to structure an asynchronous call to SendPingAsync. I want to loop through a list of IP addresses and ping them all asynchronously before moving on in the program... right now it takes forever to go through all of them one at a time. I asked a question about it earlier thinking I'd be able to figure out async but apparently I was wrong.
private void button1_Click(object sender, EventArgs e)
{
this.PingLoop();
MessageBox.Show("hi"); //for testing
}
public async void PingLoop()
{
Task<int> longRunningTask = PingAsync();
int result = await longRunningTask;
MessageBox.Show("async call is finished!");
//eventually want to loop here but for now just want to understand how this works
}
private async Task<int> PingAsync()
{
Ping pingSender = new Ping();
string reply = pingSender.SendPingAsync("www.google.com", 2000).ToString();
pingReplies.Add(reply); //what should i be awaiting here??
return 1;
}
I'm afraid I just don't get what is really going on here enough... when should I return a task? When I run this as is I just get a frozen UI and a ping error. I have read the MSDN documentation and tons of questions here and I'm just not getting it.
You'd want to do something like:
private async Task<List<PingReply>> PingAsync()
{
var tasks = theListOfIPs.Select(ip => new Ping().SendPingAsync(ip, 2000));
var results = await Task.WhenAll(tasks);
return results.ToList();
}
This will start off one request per IP in theListOfIPs asynchronously, then asynchronously wait for them all to complete. It will then return the list of replies.
Note that it's almost always better to return the results vs. setting them in a field, as well. The latter can lead to bugs if you go to use the field (pingReplies) before the asynchronous operation completes - by returning, and adding the range to your collection after the call is made with await, you make the code more clear and less bug prone.
What you do here pingSender.SendPingAsync("www.google.com", 2000).ToString(); doesn't make much sense.
Instead you should return pingSender.SendPingAsync("www.google.com", 2000) and
await Task.WhenAll(your all ping requests)
What you want is to start all pings at once:
var pingTargetHosts = ...; //fill this in
var pingTasks = pingTargetHosts.Select(
host => new Ping().SendPingAsync(host, 2000)).ToList();
Now the pings are running. Collect their results:
var pingResults = await Task.WhenAll(pingTasks);
Now the concurrent phase of the processing is done and you can examine and process the results.
Here is how I do it
private delegate void scanTargetDelegate(IPAddress ipaddress);
private Task<PingReply> pingAsync(IPAddress ipaddress)
{
var tcs = new TaskCompletionSource<PingReply>();
try
{
AutoResetEvent are = new AutoResetEvent(false);
Ping ping = new Ping();
ping.PingCompleted += (obj, sender) =>
{
tcs.SetResult(sender.Reply);
};
ping.SendAsync(ipaddress, new object { });
}
catch (Exception)
{
}
return tcs.Task;
}
in a BackgroundWorker I do this
List<Task<PingReply>> pingTasks = new List<Task<PingReply>>();
addStatus("Scanning Network");
foreach (var ip in range)
{
pingTasks.Add(pingAsync(ip));
}
Task.WaitAll(pingTasks.ToArray());
addStatus("Network Scan Complete");
scanTargetDelegate d = null;
IAsyncResult r = null;
foreach (var pingTask in pingTasks)
{
if (pingTask.Result.Status.Equals(IPStatus.Success))
{
d = new scanTargetDelegate(scanTarget); //do something with the ip
r = d.BeginInvoke(pingTask.Result.Address, null, null);
Interlocked.Increment(ref Global.queuedThreads);
}
else
{
if (!ownIPs.Contains(pingTask.Result.Address))
{
failed.Add(pingTask.Result.Address);
}
}
}
if (r != null)
{
WaitHandle[] waits = new WaitHandle[] { r.AsyncWaitHandle };
WaitHandle.WaitAll(waits);
}
public static async Task<bool> PingAsync(string host)
{
try
{
var ping = new System.Net.NetworkInformation.Ping();
var reply = await ping.SendTaskAsync(host);
return (reply.Status == System.Net.NetworkInformation.IPStatus.Success);
}
catch { return false; }
}
I'm playing with these Windows 8 WinRT tasks, and I'm trying to cancel a task using the method below, and it works to some point. The CancelNotification method DOES get called, which makes you think the task was cancelled, but in the background the task keeps running, then after it's completed, the status of the Task is always completed and never cancelled. Is there a way to completely halt the task when it's cancelled?
private async void TryTask()
{
CancellationTokenSource source = new CancellationTokenSource();
source.Token.Register(CancelNotification);
source.CancelAfter(TimeSpan.FromSeconds(1));
var task = Task<int>.Factory.StartNew(() => slowFunc(1, 2), source.Token);
await task;
if (task.IsCompleted)
{
MessageDialog md = new MessageDialog(task.Result.ToString());
await md.ShowAsync();
}
else
{
MessageDialog md = new MessageDialog("Uncompleted");
await md.ShowAsync();
}
}
private int slowFunc(int a, int b)
{
string someString = string.Empty;
for (int i = 0; i < 200000; i++)
{
someString += "a";
}
return a + b;
}
private void CancelNotification()
{
}
Read up on Cancellation (which was introduced in .NET 4.0 and is largely unchanged since then) and the Task-Based Asynchronous Pattern, which provides guidelines on how to use CancellationToken with async methods.
To summarize, you pass a CancellationToken into each method that supports cancellation, and that method must check it periodically.
private async Task TryTask()
{
CancellationTokenSource source = new CancellationTokenSource();
source.CancelAfter(TimeSpan.FromSeconds(1));
Task<int> task = Task.Run(() => slowFunc(1, 2, source.Token), source.Token);
// (A canceled task will raise an exception when awaited).
await task;
}
private int slowFunc(int a, int b, CancellationToken cancellationToken)
{
string someString = string.Empty;
for (int i = 0; i < 200000; i++)
{
someString += "a";
if (i % 1000 == 0)
cancellationToken.ThrowIfCancellationRequested();
}
return a + b;
}
Or, in order to avoid modifying slowFunc (say you don't have access to the source code for instance):
var source = new CancellationTokenSource(); //original code
source.Token.Register(CancelNotification); //original code
source.CancelAfter(TimeSpan.FromSeconds(1)); //original code
var completionSource = new TaskCompletionSource<object>(); //New code
source.Token.Register(() => completionSource.TrySetCanceled()); //New code
var task = Task<int>.Factory.StartNew(() => slowFunc(1, 2), source.Token); //original code
//original code: await task;
await Task.WhenAny(task, completionSource.Task); //New code
You can also use nice extension methods from https://github.com/StephenCleary/AsyncEx and have it looks as simple as:
await Task.WhenAny(task, source.Token.AsTask());
One case which hasn't been covered is how to handle cancellation inside of an async method. Take for example a simple case where you need to upload some data to a service get it to calculate something and then return some results.
public async Task<Results> ProcessDataAsync(MyData data)
{
var client = await GetClientAsync();
await client.UploadDataAsync(data);
await client.CalculateAsync();
return await client.GetResultsAsync();
}
If you want to support cancellation then the easiest way would be to pass in a token and check if it has been cancelled between each async method call (or using ContinueWith). If they are very long running calls though you could be waiting a while to cancel. I created a little helper method to instead fail as soon as canceled.
public static class TaskExtensions
{
public static async Task<T> WaitOrCancel<T>(this Task<T> task, CancellationToken token)
{
token.ThrowIfCancellationRequested();
await Task.WhenAny(task, token.WhenCanceled());
token.ThrowIfCancellationRequested();
return await task;
}
public static Task WhenCanceled(this CancellationToken cancellationToken)
{
var tcs = new TaskCompletionSource<bool>();
cancellationToken.Register(s => ((TaskCompletionSource<bool>)s).SetResult(true), tcs);
return tcs.Task;
}
}
So to use it then just add .WaitOrCancel(token) to any async call:
public async Task<Results> ProcessDataAsync(MyData data, CancellationToken token)
{
Client client;
try
{
client = await GetClientAsync().WaitOrCancel(token);
await client.UploadDataAsync(data).WaitOrCancel(token);
await client.CalculateAsync().WaitOrCancel(token);
return await client.GetResultsAsync().WaitOrCancel(token);
}
catch (OperationCanceledException)
{
if (client != null)
await client.CancelAsync();
throw;
}
}
Note that this will not stop the Task you were waiting for and it will continue running. You'll need to use a different mechanism to stop it, such as the CancelAsync call in the example, or better yet pass in the same CancellationToken to the Task so that it can handle the cancellation eventually. Trying to abort the thread isn't recommended.
I just want to add to the already accepted answer. I was stuck on this, but I was going a different route on handling the complete event. Rather than running await, I add a completed handler to the task.
Comments.AsAsyncAction().Completed += new AsyncActionCompletedHandler(CommentLoadComplete);
Where the event handler looks like this
private void CommentLoadComplete(IAsyncAction sender, AsyncStatus status )
{
if (status == AsyncStatus.Canceled)
{
return;
}
CommentsItemsControl.ItemsSource = Comments.Result;
CommentScrollViewer.ScrollToVerticalOffset(0);
CommentScrollViewer.Visibility = Visibility.Visible;
CommentProgressRing.Visibility = Visibility.Collapsed;
}
With this route, all the handling is already done for you, when the task is cancelled it just triggers the event handler and you can see if it was cancelled there.