I want to send a request to a server and process the returned value:
private static string Send(int id)
{
Task<HttpResponseMessage> responseTask = client.GetAsync("aaaaa");
string result = string.Empty;
responseTask.ContinueWith(x => result = Print(x));
responseTask.Wait(); // it doesn't wait for the completion of the response task
return result;
}
private static string Print(Task<HttpResponseMessage> httpTask)
{
Task<string> task = httpTask.Result.Content.ReadAsStringAsync();
string result = string.Empty;
task.ContinueWith(t =>
{
Console.WriteLine("Result: " + t.Result);
result = t.Result;
});
task.Wait(); // it does wait
return result;
}
Am I using Task correctly? I don't think so because the Send() method returns string.Empty every time, while Print returns the correct value.
What am I doing wrong? How do I get the correct result from a server?
Your Print method likely needs to wait for the continuation to finish (ContinueWith returns a task which you can wait on). Otherwise the second ReadAsStringAsync finishes, the method returns (before result is assigned in the continuation). Same problem exists in your send method. Both need to wait on the continuation to consistently get the results you want. Similar to below
private static string Send(int id)
{
Task<HttpResponseMessage> responseTask = client.GetAsync("aaaaa");
string result = string.Empty;
Task continuation = responseTask.ContinueWith(x => result = Print(x));
continuation.Wait();
return result;
}
private static string Print(Task<HttpResponseMessage> httpTask)
{
Task<string> task = httpTask.Result.Content.ReadAsStringAsync();
string result = string.Empty;
Task continuation = task.ContinueWith(t =>
{
Console.WriteLine("Result: " + t.Result);
result = t.Result;
});
continuation.Wait();
return result;
}
It waits for client.GetAsync("aaaaa");, but doesn't wait for result = Print(x)
Try responseTask.ContinueWith(x => result = Print(x)).Wait()
--EDIT--
Task responseTask = Task.Run(() => {
Thread.Sleep(1000);
Console.WriteLine("In task");
});
responseTask.ContinueWith(t=>Console.WriteLine("In ContinueWith"));
responseTask.Wait();
Console.WriteLine("End");
Above code doesn't guarantee the output:
In task
In ContinueWith
End
But this does (see the newTask)
Task responseTask = Task.Run(() => {
Thread.Sleep(1000);
Console.WriteLine("In task");
});
Task newTask = responseTask.ContinueWith(t=>Console.WriteLine("In ContinueWith"));
newTask.Wait();
Console.WriteLine("End");
A clean example that answers the Title
string output = "Error";
Task task = Task.Factory.StartNew(() =>
{
System.Threading.Thread.Sleep(2000);
output = "Complete";
});
task.Wait();
Console.WriteLine(output);
I'm an async novice, so I can't tell you definitively what is happening here. I suspect that there's a mismatch in the method execution expectations, even though you are using tasks internally in the methods. I think you'd get the results you are expecting if you changed Print to return a Task<string>:
private static string Send(int id)
{
Task<HttpResponseMessage> responseTask = client.GetAsync("aaaaa");
Task<string> result;
responseTask.ContinueWith(x => result = Print(x));
result.Wait();
responseTask.Wait(); // There's likely a better way to wait for both tasks without doing it in this awkward, consecutive way.
return result.Result;
}
private static Task<string> Print(Task<HttpResponseMessage> httpTask)
{
Task<string> task = httpTask.Result.Content.ReadAsStringAsync();
string result = string.Empty;
task.ContinueWith(t =>
{
Console.WriteLine("Result: " + t.Result);
result = t.Result;
});
return task;
}
When working with continuations I find it useful to think of the place where I write .ContinueWith as the place from which execution immediately continues to the statements following it, not the statements 'inside' it. In that case it becomes clear that you would get an empty string returned in Send. If your only processing of the response is writing it to the console, you don't need any Wait in Ito's solution - the console printout will happen without waits but both Send and Print should return void in that case. Run this in console app and you will get printout of the page.
IMO, waits and Task.Result calls (which block) are necessary sometimes, depending on your desired flow of control, but more often they are a sign that you don't really use asynchronous functionality correctly.
namespace TaskTest
{
class Program
{
static void Main(string[] args)
{
Send();
Console.WriteLine("Press Enter to exit");
Console.ReadLine();
}
private static void Send()
{
HttpClient client = new HttpClient();
Task<HttpResponseMessage> responseTask = client.GetAsync("http://google.com");
responseTask.ContinueWith(x => Print(x));
}
private static void Print(Task<HttpResponseMessage> httpTask)
{
Task<string> task = httpTask.Result.Content.ReadAsStringAsync();
Task continuation = task.ContinueWith(t =>
{
Console.WriteLine("Result: " + t.Result);
});
}
}
}
async Task<int> AccessTheWebAsync()
{
// You need to add a reference to System.Net.Http to declare client.
HttpClient client = new HttpClient();
// GetStringAsync returns a Task<string>. That means that when you await the
// task you'll get a string (urlContents).
Task<string> getStringTask =
client.GetStringAsync("http://msdn.microsoft.com");
// You can do work here that doesn't rely on the string from GetStringAsync.
DoIndependentWork();
// The await operator suspends AccessTheWebAsync.
// - AccessTheWebAsync can't continue until getStringTask is complete.
// - Meanwhile, control returns to the caller of AccessTheWebAsync.
// - Control resumes here when getStringTask is complete.
// - The await operator then retrieves the string result from
getStringTask.
string urlContents = await getStringTask;
// The return statement specifies an integer result.
// Any methods that are awaiting AccessTheWebenter code hereAsync retrieve the length
value.
return urlContents.Length;
}
Related
I am testing web socket subscriptions in my tests and I would like to wait for response from callback and then end the test, if no response is received after timeout end the test.
This is what I have now (simplified) but I am not sure if its the way how to do it.
public async Task WaitForPing()
{
var cancellationTokenSource = new CancellationTokenSource(5_000);
var pinged = false;
using var _ = Client.OnPing(_ =>
{
pinged = true;
cancellationTokenSource.Cancel();
}
await Client.Run();
await Task
.Delay(-1, cancellationTokenSource.Token)
.ContinueWith(_ => { }, CancellationToken.None);
Assert(pinged);
}
A proper method should be like that:
static Task<string> WaitForResponseAsync(CancellationTokenSource cancellationTokenSource = default)
{
var tcs = new TaskCompletionSource<string>();
// Register a method that throws an exception when task cancelled.
cancellationTokenSource.Token.Register(()=> throw new Exception("Timed out!"));
// Replace this task with your async operation. Like OnPing(_ => ...
Task.Run(async () =>
{
await Task.Delay(30_000); // Response will be received after 30 seconds
tcs.SetResult("Hello World");
});
return tcs.Task; // Return awaitable task
}
And place where you call that method:
try
{
Console.WriteLine(await WaitForResponseAsync(new CancellationTokenSource(5_000))); // Time out is 5 seconds
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
Don't worry about the task in a task, you should replace it with your event. So if I customize it according to your method, it should be something like that:
Task WaitForPing(CancellationTokenSource cancellationTokenSource)
{
var tcs = new TaskCompletionSource();
cancellationTokenSource.Token.Register(() => throw new Exception("Timed out"));
// Or just call SetResult to finish the task before completed without exception:
// cancellationTokenSource.Token.Register(() => tcs.SetResult());
// (Personally I do not recommend this one)
using var _ = Client.OnPing(_ =>
{
tcs.SetResult();
};
return tcs.Task;
}
when will come Task.IsCanceled = true;
Code:
var cts = new CancellationTokenSource();
string result = "";
cts.CancelAfter(10000);
try
{
Task t = Task.Run(() =>
{
using (var stream = new WebClient().OpenRead("http://www.rediffmail.com"))
{
result = "success!";
}
cts.Token.ThrowIfCancellationRequested();
}, cts.Token);
Stopwatch timer = new Stopwatch();
timer.Start();
while (timer.IsRunning)
{
if (timer.ElapsedMilliseconds <= 10000)
{
if (result != ""){
timer.Stop();
Console.WriteLine(result);
}
}
else
{
timer.Stop();
//cts.Cancel();
//cts.Token.ThrowIfCancellationRequested();
}
}
}
catch (OperationCanceledException)
{
Console.WriteLine(t.IsCanceled); // still its appear in false.
}
My requirement is - Task is not completed upto 10seconds, Need to cancel the task.
So I am setting timer and watch upto the given seconds. its not completed mean cancel the task and showing error message.
You have to pass the token to your method. It should inspect the token and respect the call to Cancel() of the CancellationTokenSource.
Or you do it yourself:
Task t = Task.Factory.StartNew(() =>
{
myResult = method(); // Request processing in parallel
cts.Token.ThrowIfCancellationRequested(); // React on cancellation
}, cts.Token);
A complete example is this:
async Task Main()
{
var cts = new CancellationTokenSource();
var ct = cts.Token;
cts.CancelAfter(500);
Task t = null;
try
{
t = Task.Run(() => { Thread.Sleep(1000); ct.ThrowIfCancellationRequested(); }, ct);
await t;
Console.WriteLine(t.IsCanceled);
}
catch (OperationCanceledException)
{
Console.WriteLine(t.IsCanceled);
}
}
The output is that an OperationCanceledException is thrown and the result is
True
if you remove the ct.ThrowIfCancellationRequested(); part it will show
False
Edit:
Now, you have updated the question, some comments on that. First, you won't need the timer anymore since you are using the CancelAfter method. Second, you need to await your task. So that makes something like this:
string result = "";
cts.CancelAfter(10000);
Task t = null;
try
{
t = Task.Run(() =>
{
using (var stream = new WebClient().OpenRead("http://www.rediffmail.com"))
{
cts.Token.ThrowIfCancellationRequested();
result = "success!";
}
}, cts.Token);
await t;
}
catch (OperationCanceledException)
{
Console.WriteLine(t.IsCanceled);
}
This should show that t.IsCanceled is true but of course only when the call of the WebClient takes longer that 10 seconds.
From documentation:
A Task will complete in the TaskStatus.Canceled state under any of the following conditions:
Its CancellationToken was marked for cancellation before the task started executing.
The task acknowledged the cancellation request on its already signaled CancellationToken by throwing an OperationCanceledException that bears the same CancellationToken.
The task acknowledged the cancellation request on its already signaled CancellationToken by calling the ThrowIfCancellationRequested method on the CancellationToken.
So basically you would need to throw an OperationCanceledException within your task to force the state for instance by executing cts.Token.ThrowIfCancellationRequested() just after you cancel it.
But the intention of this mechanism is a bit the other way around. You cancel source say while user presses cancel button on your form (from outside of your task) an task just verifies if cancellation was requested in some safe to cancel points of its code.
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'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.