Dispatcher in WPF apps implementing multiple async Tasks - c#

In the following MSDN example of the WPF app, which demonstrates async/await implementation of the multiple async Tasks, the Dispatcher object apparently is not used/needed, i.e. asynchronously executed Tasks seem to have direct access to the UI controls (in this case resultTextBox TextBox control - see the line resultsTextBox.Text += String.Format("\r\nLength of the download: {0}", length);). The app has been tested, performing as expected.
However, the question still remains if this implementation is capable of proper handling the possible race condition, for e.g., if the awaited and completed Task tries to access that TextBox control while the latter is still processing the update from previously completed Task? In practical sense, is WPF Dispatcher object still required to handle this potential concurrency/race condition issues in async/await multitasking implementation (or, may be, the interlocking functionality has been somehow implicitly implemented in such async/await programming construct)?
Listing 1. MSDN article "Start Multiple Async Tasks and Process Them As They Complete" (https://msdn.microsoft.com/en-us/library/jj155756.aspx)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
// Add a using directive and a reference for System.Net.Http.
using System.Net.Http;
// Add the following using directive.
using System.Threading;
namespace ProcessTasksAsTheyFinish
{
public partial class MainWindow : Window
{
// Declare a System.Threading.CancellationTokenSource.
CancellationTokenSource cts;
public MainWindow()
{
InitializeComponent();
}
private async void startButton_Click(object sender, RoutedEventArgs e)
{
resultsTextBox.Clear();
// Instantiate the CancellationTokenSource.
cts = new CancellationTokenSource();
try
{
await AccessTheWebAsync(cts.Token);
resultsTextBox.Text += "\r\nDownloads complete.";
}
catch (OperationCanceledException)
{
resultsTextBox.Text += "\r\nDownloads canceled.\r\n";
}
catch (Exception)
{
resultsTextBox.Text += "\r\nDownloads failed.\r\n";
}
cts = null;
}
private void cancelButton_Click(object sender, RoutedEventArgs e)
{
if (cts != null)
{
cts.Cancel();
}
}
async Task AccessTheWebAsync(CancellationToken ct)
{
HttpClient client = new HttpClient();
// Make a list of web addresses.
List<string> urlList = SetUpURLList();
// ***Create a query that, when executed, returns a collection of tasks.
IEnumerable<Task<int>> downloadTasksQuery =
from url in urlList select ProcessURL(url, client, ct);
// ***Use ToList to execute the query and start the tasks.
List<Task<int>> downloadTasks = downloadTasksQuery.ToList();
// ***Add a loop to process the tasks one at a time until none remain.
while (downloadTasks.Count > 0)
{
// Identify the first task that completes.
Task<int> firstFinishedTask = await Task.WhenAny(downloadTasks);
// ***Remove the selected task from the list so that you don't
// process it more than once.
downloadTasks.Remove(firstFinishedTask);
// Await the completed task.
int length = await firstFinishedTask;
resultsTextBox.Text += String.Format("\r\nLength of the download: {0}", length);
}
}
private List<string> SetUpURLList()
{
List<string> urls = new List<string>
{
"http://msdn.microsoft.com",
"http://msdn.microsoft.com/library/windows/apps/br211380.aspx",
"http://msdn.microsoft.com/en-us/library/hh290136.aspx",
"http://msdn.microsoft.com/en-us/library/dd470362.aspx",
"http://msdn.microsoft.com/en-us/library/aa578028.aspx",
"http://msdn.microsoft.com/en-us/library/ms404677.aspx",
"http://msdn.microsoft.com/en-us/library/ff730837.aspx"
};
return urls;
}
async Task<int> ProcessURL(string url, HttpClient client, CancellationToken ct)
{
// GetAsync returns a Task<HttpResponseMessage>.
HttpResponseMessage response = await client.GetAsync(url, ct);
// Retrieve the website contents from the HttpResponseMessage.
byte[] urlContents = await response.Content.ReadAsByteArrayAsync();
return urlContents.Length;
}
}
}
Note: I would like to thank Stephen Cleary for his excellent answer and rather insightful explanation, and also want to highlight the recommended improvement outlined in his solution, namely: replacing that unnecessary/complex code block in original MSDN example utilizing WhenAny by rather compact solution encapsulated in a single line of code, namely: await Task.WhenAll(downloadTasks); (btw, I was using this alternative in many practical apps, in particular, online market data app dealing w/multiple stocks web queries).
Many thanks, Stephen!

However, the question still remains if this implementation is capable of proper handling the possible race condition, for e.g., if the awaited and completed Task tries to access that TextBox control while the latter is still processing the update from previously completed Task?
There is no race condition. The UI thread only does one thing at a time.
In practical sense, is WPF Dispatcher object still required to handle this potential concurrency/race condition issues in async/await multitasking implementation (or, may be, the interlocking functionality has been somehow implicitly implemented in such async/await programming construct)?
It is, but you don't have to use it explicitly. As I describe in my async intro, the await keyword (by default) will capture the current context and resume executing the async method in that context. The "context" is SynchronizationContext.Current (or TaskScheduler.Current if the current SyncCtx is null).
In this case, it will capture a UI SynchronizationContext, which uses the WPF Dispatcher under the covers to schedule the remainder of the async method on the UI thread.
On a side note, I'm not a big fan of the "Task.WhenAny the list and remove from the list as they complete" approach. I find the code is much cleaner if you refactor by adding a DownloadAndUpdateAsync method:
async Task AccessTheWebAsync(CancellationToken ct)
{
HttpClient client = new HttpClient();
// Make a list of web addresses.
List<string> urlList = SetUpURLList();
// ***Create a query that, when executed, returns a collection of tasks.
IEnumerable<Task> downloadTasksQuery =
from url in urlList select DownloadAndUpdateAsync(url, client, ct);
// ***Use ToList to execute the query and start the tasks.
List<Task> downloadTasks = downloadTasksQuery.ToList();
await Task.WhenAll(downloadTasks);
}
async Task DownloadAndUpdateAsync(string url, HttpClient client, CancellationToken ct)
{
var length = await ProcessURLAsync(url, client, ct);
resultsTextBox.Text += String.Format("\r\nLength of the download: {0}", length);
}
async Task<int> ProcessURLAsync(string url, HttpClient client, CancellationToken ct)
{
// GetAsync returns a Task<HttpResponseMessage>.
HttpResponseMessage response = await client.GetAsync(url, ct);
// Retrieve the website contents from the HttpResponseMessage.
byte[] urlContents = await response.Content.ReadAsByteArrayAsync();
return urlContents.Length;
}

Related

Await not returning in SpecFlow async test step

I'm having trouble with some async SpecFlow unit tests where an await is never returning. I think that it is to do with the SpecFlow SynchronizationContext, but I'm not really sure how this works.
The class I am writing tests for periodically runs code on an IScheduler. This code awaits on a Task that completes on an external event. In the unit test the IScheduler is a TestScheduler and the external event Task is simulated by some stub code.
The only way I have been able to get this to work, is to ensure that the await and the task completion are both called from synchronous SpecFlow test steps.
If the await is called from an asynchronous test step then the await never returns.
If the await is called from a synchronous test step and the task completion is called from an asynchronous test step then the await will return, but on a worker thread, so not under control of the unit test, so I have to delay to ensure it happens.
If both the await and the task completion are called from synchronous test steps then the await will return synchronously on the same thread as the unit test.
My assumption is that when the await is called from an asynchronous test step then SynchronizationContext.Current is captured and when the task completes it tries to return on that SynchronizationContext, but it is no longer valid because it was a previous test step. When called from a synchronous test step I noticed that SynchronizationContext.Current is null, which I assume is why the await returns synchronously with the task completion (which is what I want for testing)
Is there something I can do with the await and/or task completion (both are in the test stub code) to ensure the await ignores SynchronizationContext.Current and always returns synchronously in the test?
I'm using SpecFlow 3.9.58
I was able to reproduce this issue with the following code:
Feature: Await
Scenario: Wait Async, Set Async
Given we await asynchronously
When we set asynchronously
Then await should have completed
Scenario: Wait Sync, Set Sync
Given we await synchronously
When we set synchronously
Then await should have completed
Scenario: Wait Async, Set Sync
Given we await asynchronously
When we set synchronously
Then await should have completed
Scenario: Wait Sync, Set Async
Given we await synchronously
When we set asynchronously
Then await should have completed
Scenario: Wait Sync, Set Async, Delay
Given we await synchronously
When we set asynchronously
And we wait a bit
Then await should have completed
using Microsoft.Reactive.Testing;
using NUnit.Framework;
using System.Reactive.Concurrency;
using System.Threading;
using System.Threading.Tasks;
using TechTalk.SpecFlow;
namespace Test.SystemTests
{
[Binding]
public sealed class AwaitSteps
{
private TestScheduler _scheduler = new TestScheduler();
private TaskCompletionSource _tcs;
private bool _waiting;
// The previous Wait() implementation before Zer0's answer
//private async Task Wait()
//{
// _tcs = new TaskCompletionSource();
// _waiting = true;
// await _tcs.Task;
// _waiting = false;
//}
// Updated Wait()/NestedWait() following Zer0's answer
private async Task NestedWait()
{
// Simulate the code in the stub class (can change this)
_tcs = new TaskCompletionSource();
await _tcs.Task.ConfigureAwait(false);
}
private async Task Wait()
{
// Simulate the code in the class under test (prefer not to change this)
_waiting = true;
await NestedWait();
_waiting = false;
}
private void WaitOnScheduler()
{
_scheduler.Schedule(async () => await Wait());
_scheduler.AdvanceBy(1);
}
private void Set() => _tcs.TrySetResult();
[Given(#"we await asynchronously")]
public async Task GivenWeAwaitAsynchronously()
{
await Task.CompletedTask; // Just to make the step async
WaitOnScheduler();
}
[Given(#"we await synchronously")]
public void GivenWeAwaitSynchronously() => WaitOnScheduler();
[When(#"we set asynchronously")]
public async Task WhenWeSetAsynchronously()
{
await Task.CompletedTask; // Just to make the step async
Set();
}
[When(#"we set synchronously")]
public void WhenWeSetSynchronously() => Set();
[When(#"we wait a bit")]
public async Task WhenWeWaitABit() => await Task.Delay(100);
[Then(#"await should have completed")]
public void ThenAwaitShouldHaveCompleted() => Assert.AreEqual(false, _waiting);
}
}
My assumption is that when the await is called from an asynchronous test step then SynchronizationContext.Current is captured and when the task completes it tries to return on that SynchronizationContext
That can be changed with ConfigureAwait(false). I made that change alone:
private async Task Wait()
{
_tcs = new TaskCompletionSource();
_waiting = true;
await _tcs.Task.ConfigureAwait(false);
_waiting = false;
}
And got these results, using your code verbatim minus that one change.
Regarding having to call ConfigureAwait(false) all the time there are several workarounds. Here's useful documentation around all of this, including the details and ways to avoid capture such as a simple Task.Run call.
The easiest in this case is doing this as you setup your scenario:
SynchronizationContext.SetSynchronizationContext(null);
This eliminates the need for ConfigureAwait entirely as there's nothing to capture.
There are other options. See the linked article or comment if this doesn't answer your question.
AFAIK, there is no exposed property anywhere to change the default behavior, so some code is required regardless.
That said ConfigureAwait(false) makes it very explicit what you're doing when other coders read it. So I'd keep that in mind if you want a different solution.

Locking issue with LimitedConcurrencyLevelTaskScheduler and aync/await

I'm struggling to understand what's happening in this simple program.
In the example below I have a task factory that uses the LimitedConcurrencyLevelTaskScheduler from ParallelExtensionsExtras with maxDegreeOfParallelism set to 2.
I then start 2 tasks that each call an async method (e.g. an async Http request), then gets the awaiter and the result of the completed task.
The problem seem to be that Task.Delay(2000) never completes. If I set maxDegreeOfParallelism to 3 (or greater) it completes. But with maxDegreeOfParallelism = 2 (or less) my guess is that there is no thread available to complete the task. Why is that?
It seems to be related to async/await since if I remove it and simply do Task.Delay(2000).GetAwaiter().GetResult() in DoWork it works perfectly. Does async/await somehow use the parent task's task scheduler, or how is it connected?
using System;
using System.Linq;
using System.Threading.Tasks;
using System.Threading.Tasks.Schedulers;
namespace LimitedConcurrency
{
class Program
{
static void Main(string[] args)
{
var test = new TaskSchedulerTest();
test.Run();
}
}
class TaskSchedulerTest
{
public void Run()
{
var scheduler = new LimitedConcurrencyLevelTaskScheduler(2);
var taskFactory = new TaskFactory(scheduler);
var tasks = Enumerable.Range(1, 2).Select(id => taskFactory.StartNew(() => DoWork(id)));
Task.WaitAll(tasks.ToArray());
}
private void DoWork(int id)
{
Console.WriteLine($"Starting Work {id}");
HttpClientGetAsync().GetAwaiter().GetResult();
Console.WriteLine($"Finished Work {id}");
}
async Task HttpClientGetAsync()
{
await Task.Delay(2000);
}
}
}
Thanks in advance for any help
await by default captures the current context and uses that to resume the async method. This context is SynchronizationContext.Current, unless it is null, in which case it is TaskScheduler.Current.
In this case, await is capturing the LimitedConcurrencyLevelTaskScheduler used to execute DoWork. So, after starting the Task.Delay both times, both of those threads are blocked (due to the GetAwaiter().GetResult()). When the Task.Delay completes, the await schedules the remainder of the HttpClientGetAsync method to its context. However, the context will not run it since it already has 2 threads.
So you end up with threads blocked in the context until their async methods complete, but the async methods cannot complete until there is a free thread in the context; thus a deadlock. Very similar to the standard "don't block on async code" style of deadlock, just with n threads instead of one.
Clarifications:
The problem seem to be that Task.Delay(2000) never completes.
Task.Delay is completing, but the await cannot continue executing the async method.
If I set maxDegreeOfParallelism to 3 (or greater) it completes. But with maxDegreeOfParallelism = 2 (or less) my guess is that there is no thread available to complete the task. Why is that?
There are plenty of threads available. But the LimitedConcurrencyTaskScheduler only allows 2 threads at a time to run in its context.
It seems to be related to async/await since if I remove it and simply do Task.Delay(2000).GetAwaiter().GetResult() in DoWork it works perfectly.
Yes; it's the await that is capturing the context. Task.Delay does not capture a context internally, so it can complete without needing to enter the LimitedConcurrencyTaskScheduler.
Solution:
Task schedulers in general do not work very well with asynchronous code. This is because task schedulers were designed for Parallel Tasks rather than asynchronous tasks. So they only apply when code is running (or blocked). In this case, LimitedConcurrencyLevelTaskScheduler only "counts" code that's running; if you have a method that's doing an await, it won't "count" against that concurrency limit.
So, your code has ended up in a situation where it has the sync-over-async antipattern, probably because someone was trying to avoid the problem of await not working as expected with limited concurrency task schedulers. This sync-over-async antipattern has then caused the deadlock problem.
Now, you could add in more hacks by using ConfigureAwait(false) everywhere and continue blocking on asynchronous code, or you could fix it better.
A more proper fix would be to do asynchronous throttling. Toss out the LimitedConcurrencyLevelTaskScheduler completely; concurrency-limiting task schedulers only work with synchronous code, and your code is asynchronous. You can do asynchronous throttling using SemaphoreSlim, as such:
class TaskSchedulerTest
{
private readonly SemaphoreSlim _mutex = new SemaphoreSlim(2);
public async Task RunAsync()
{
var tasks = Enumerable.Range(1, 2).Select(id => DoWorkAsync(id));
await Task.WhenAll(tasks);
}
private async Task DoWorkAsync(int id)
{
await _mutex.WaitAsync();
try
{
Console.WriteLine($"Starting Work {id}");
await HttpClientGetAsync();
Console.WriteLine($"Finished Work {id}");
}
finally
{
_mutex.Release();
}
}
async Task HttpClientGetAsync()
{
await Task.Delay(2000);
}
}
I think you are encountering a sync deadlock. You are waiting for a thread to complete that is waiting for your thread to complete. Never going to happen. If you make your DoWork method async so you can await the HttpClientGetAsync() call, and you'll avoid the deadlock.
using MassTransit.Util;
using System;
using System.Linq;
using System.Threading.Tasks;
//using System.Threading.Tasks.Schedulers;
namespace LimitedConcurrency
{
class Program
{
static void Main(string[] args)
{
var test = new TaskSchedulerTest();
test.Run();
}
}
class TaskSchedulerTest
{
public void Run()
{
var scheduler = new LimitedConcurrencyLevelTaskScheduler(2);
var taskFactory = new TaskFactory(scheduler);
var tasks = Enumerable.Range(1, 2).Select(id => taskFactory.StartNew(() => DoWork(id)));
Task.WaitAll(tasks.ToArray());
}
private async Task DoWork(int id)
{
Console.WriteLine($"Starting Work {id}");
await HttpClientGetAsync();
Console.WriteLine($"Finished Work {id}");
}
async Task HttpClientGetAsync()
{
await Task.Delay(2000);
}
}
}
https://medium.com/rubrikkgroup/understanding-async-avoiding-deadlocks-e41f8f2c6f5d
TLDR never call .result, which I'm sure .GetResult(); was doing

When to use Task.Run and not

I asked this question yesterday and still don't understand the difference in using
task = Task.Run(() => RunLongRunningMethod(cts.Token));
and
task = RunLongRunningMethod(cts.Token);
I've read through Task.Run Etiquette and Proper Usage and it appears to be mostly using Task.Run as long as it's used correctly (not in the implementation)
Is there any other reading material about this and or can someone explain the difference between the two?
My code is below which works okay using both methods.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Drawing;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace WindowsFormsApp1
{
public partial class Form1 : Form
{
private static HttpClient client { get; set; }
private Task task { get; set; }
private CancellationTokenSource cts { get; set; }
private bool buttonStartStopState { get; set; }
public Form1()
{
InitializeComponent();
}
private async Task RunLongRunningMethod(CancellationToken cancellationToken)
{
try
{
while (true)
{
if (cancellationToken.IsCancellationRequested)
{
cancellationToken.ThrowIfCancellationRequested();
}
// Some CPU bound work here
// Then call async
var success = await GetUrlAsync(#"https://www.bbc.co.uk/");
Thread.Sleep(2000); // simulate blocking only
}
}
catch (OperationCanceledException)
{
// Just exit without logging. Operation cancelled by user.
}
catch (Exception ex)
{
// Report Error
}
}
private async Task<bool> GetUrlAsync(string url)
{
if (client == null)
{
client = new HttpClient(new HttpClientHandler() { UseDefaultCredentials = true, AutomaticDecompression = DecompressionMethods.Deflate | DecompressionMethods.GZip });
client.BaseAddress = new Uri("https://www.bbc.co.uk/");
client.DefaultRequestHeaders.Add("Accept", "*/*");
client.DefaultRequestHeaders.AcceptEncoding.Add(new StringWithQualityHeaderValue("gzip"));
client.DefaultRequestHeaders.Add("User-Agent", "Mozilla/5.0 (Windows NT 10.0; …) Gecko/20100101 Firefox/62.0");
client.DefaultRequestHeaders.Connection.Add("Keep-Alive");
client.DefaultRequestHeaders.Add("DNT", "1");
}
var response = await client.GetAsync(url);
var contents = await response.Content.ReadAsStringAsync();
Debug.WriteLine($"{DateTime.Now} {response.StatusCode}");
return true;
}
private void buttonStartStop_Click(object sender, EventArgs e)
{
buttonStartStopState = !buttonStartStopState;
if(buttonStartStopState)
{
cts = new CancellationTokenSource();
// What is difference between this
//task = Task.Run(() => RunLongRunningMethod(cts.Token));
// And this?
task = RunLongRunningMethod(cts.Token);
// This always runs instantly
Debug.WriteLine($"{DateTime.Now} buttonStartStopState:{buttonStartStopState}");
}
else
{
cts.Cancel();
cts = null;
}
}
private async void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
if(cts != null)
{
cts.Cancel(); // CancellationTokenSource
cts = null;
}
if (!task.IsCompleted)
{
//this.Hide();
e.Cancel = true;
await task;
this.Close();
}
}
}
}
Basically, use async/await throughout your code base so that nothing is blocking threads. (e.g. your current use of Thread.Sleep is blocking, as I commented).
Once you've reached that point, you've already got hot Tasks returned by your async functions, and these functions will return as soon as they're not making further progress.
At this point, you have a decision to make. Do you have a long running task that is CPU bound? If so, that is when you might consider using Task.Run, because that explicitly asks for the work to be done elsewhere (the thread pool). It's generally ok to allow I/O dominated tasks to come back onto the UI thread briefly, which is what you'll get by default and means that you don't have to do anything special to access UI objects.
Hopefully, at this point, you'll not want to use Task.Run at all in your example.
But it is possible that your long running task is a combination of truly async I/O operations and some CPU intensive operations, and you still don't want those occupying the UI thread. At this point, you should normally be considering using ConfigureAwait(false) on your awaitables. But you may wish to also use Task.Run here.
In either case, if you're wanting to interact with UI objects again, you'll have to Invoke to get back onto the UI thread. Make sure you do this at the right "granularity". Don't Invoke 5 or 6 separate basic setting of properties on UI objects. But also don't Invoke back onto the UI thread before actually doing the CPU intensive operations - they're why you tried to move them to a different thread in the first place!
Just to add to the 2 previous answers
Answer by #Neil
Answer by #Thierry V
It looks like you are running inside WinForms which runs in STA (Single Threaded Apartment) model, meaning there is only thread processing UI queued messages.
Therefore when you run task = Task.Run(() => RunLongRunningMethod(cts.Token)); this does everything on a thread pool thread, where as by default the task = RunLongRunningMethod(cts.Token); will block the UI for the duration of Thread.Sleep(2000); // simulate blocking only as your await call will be queued onto the the UI Dispatcher as you do not have ConfigureAwait(false).
Overall not running it on a background thread means:
Your Thread.Sleep(x) or the actual work time taken will block the UI
You will put more pressure on the dispatcher as each await will be scheduled to be executed by the UI dispatcher. (in your instance if it is only a single await, it is not an issue, but if you started to do 100's or even 1000's awaits, this can start showing noticeable performance loss!)
task = Task.Run(() => RunLongRunningMethod(cts.Token));
Will queue RunLongRunningMethod to be executed by one of the threads in the task pool at some point in the future. The next line of code will be executed immediately.
Whereas
RunLongRunningMethod(cts.Token);
Will execute the code on the current thread, and will not run the next line of code until it has finished.
RunLongRunningMethod(cts.Token); execute your action immediately in the same thread. That means it can block your UI if your code is in the UI thread.
task = Task.Run(() => RunLongRunningMethod(cts.Token)); contrariwise means that you want to execute right away your action. This line queues the task to run on the ThreadPool and returns a task handle for that work.
Normally, we use:
Task.Run when you want to execute a long-running code and don't wait if the task finishes. a calculation in background i.e and
task = RunLongRunningMethod normally when you want to await task, i.e
await RunLongRunningMethod(token);
//this method DoSomeThing is not executed until your your long-running code finishs
DoSomeThing();

How to create HttpWebRequest without interrupting async/await?

I have a bunch of slow functions that are essentially this:
private async Task<List<string>> DownloadSomething()
{
var request = System.Net.WebRequest.Create("https://valid.url");
...
using (var ss = await request.GetRequestStreamAsync())
{
await ss.WriteAsync(...);
}
using (var rr = await request.GetResponseAsync())
using (var ss = rr.GetResponseStream())
{
//read stream and return data
}
}
This works nicely and asynchronously except for the call to WebRequest.Create - this single line freezes the UI thread for several seconds which sort of ruins the purpose of async/await.
I already have this code written using BackgroundWorkers, which works perfectly and never freezes the UI.
Still, what is the correct, idiomatic way to create a web request with respect to async/await? Or maybe there is another class that should be used?
I've seen this nice answer about asyncifying a WebRequest, but even there the object itself is created synchronously.
Interestingly, I'm not seeing a blocking delay with WebRequest.Create or HttpClient.PostAsync. It might be something to do with DNS resolution or proxy configuration, although I'd expect these operations to be implemented internally as asynchronous, too.
Anyway, as a workaround you can start the request on a pool thread, although this is not something I'd normally do:
private async Task<List<string>> DownloadSomething()
{
var request = await Task.Run(() => {
// WebRequest.Create freezes??
return System.Net.WebRequest.Create("https://valid.url");
});
// ...
using (var ss = await request.GetRequestStreamAsync())
{
await ss.WriteAsync(...);
}
using (var rr = await request.GetResponseAsync())
using (var ss = rr.GetResponseStream())
{
//read stream and return data
}
}
That would keep the UI responsive, but it might be difficult to cancel it if user wants to stop the operation. That's because you need to already have a WebRequest instance to be able to call Abort on it.
Using HttpClient, cancellation would be possible, something like this:
private async Task<List<string>> DownloadSomething(CancellationToken token)
{
var httpClient = new HttpClient();
var response = await Task.Run(async () => {
return await httpClient.PostAsync("https://valid.url", token);
}, token);
// ...
}
With HttpClient, you can also register a httpClient.CancelPendingRequests() callback on the cancellation token, like this.
[UPDATE] Based on the comments: in your original case (before introducing Task.Run) you probably did not need the IProgress<I> pattern. As long as DownloadSomething() was called on the UI thread, every execution step after each await inside DownloadSomething would be resumed on the same UI thread, so you could just update the UI directly in between awaits.
Now, to run the whole DownloadSomething() via Task.Run on a pool thread, you would have to pass an instance of IProgress<I> into it, e.g.:
private async Task<List<string>> DownloadSomething(
string url,
IProgress<int> progress,
CancellationToken token)
{
var request = System.Net.WebRequest.Create(url);
// ...
using (var ss = await request.GetRequestStreamAsync())
{
await ss.WriteAsync(...);
}
using (var rr = await request.GetResponseAsync())
using (var ss = rr.GetResponseStream())
{
// read stream and return data
progress.Report(...); // report progress
}
}
// ...
// Calling DownloadSomething from the UI thread via Task.Run:
var progressIndicator = new Progress<int>(ReportProgress);
var cts = new CancellationTokenSource(30000); // cancel in 30s (optional)
var url = "https://valid.url";
var result = await Task.Run(() =>
DownloadSomething(url, progressIndicator, cts.Token), cts.Token);
// the "result" type is deduced to "List<string>" by the compiler
Note, because DownloadSomething is an async method itself, it is now run as a nested task, which Task.Run transparently unwraps for you. More on this: Task.Run vs Task.Factory.StartNew.
Also check out: Enabling Progress and Cancellation in Async APIs.
I think you need to use HttpClient.GetAsync() which returns a task from an HTTP request.
http://msdn.microsoft.com/en-us/library/hh158912(v=vs.110).aspx
It may depend a bit on what you want to return, but the HttpClient has a whole bunch of async methods for requests.

await asynchronous method where completion is signalled by event handler

I am working with the Contacts object in Windows Phone 8, calling SearchAysnc from within an async method. SearchAsync requires that a handler be subscribed to the SearchCompleted event, and delivers its results via one of the event args, which the async method requires to do its job (which includes invoking other async methods).
How do you await the asynchronous completion of an event i.e. bridge between the event pattern and the async/await pattern?
The only solution I could come up with was to use an EventWaitHandle, waiting on it within an awaited Task something like this:
using System.Threading;
async Task<string> MyMethod()
{
string result = null;
Contacts cons = new Contacts();
EventWaitHandle handle = new EventWaitHandle(false,EventResetMode.ManualReset);
cons.SearchCompleted += (sender,args) =>
{
// I do all my work on the results of the contact search in this handler
result = SomeOtherSynchronousOperation(args.Results);
// When I'm done, I release the thread which was waiting for the results
handle.Set();
};
cons.SearchAsync(String.Empty, FilterKind.None, "My Contact");
// I can't block this thread (or can I?)
// So, I launch a task whose sole job is to wait
await Task.Run(()=>
{
// This gets released by the Contacts.SearchCompleted handler when its work is finished,
// so that MyMethod can finish up and deliver its result
handle.WaitOne();
}
await DoOtherStuffWithResult(result);
return result;
}
My actual solution (not exactly as shown above) does work. Although the above code doesn't precisely represent the implemented solution, (likely a compile issue or two), it should serve to express the concept and illustrate the point of my question.
It leaves me wondering if this is the only way, or anywhere close to the best practise way to await the execution of an event handler, and if not, what would be the "best practice" to do what is needed here.
Do the Windows synchronization primitives still have a place in an async/await world?
(Based on answers provided)
Would this be correct?
using Microsoft.Phone.UserData;
string ExtractWhatIWantFromResults(IEnumerable<Contact> results)
{
string result;
// Do my processing on the list of contacts, stuff the results into result
return string;
}
async Task<string> MyMethod()
{
Contacts cons = new Contacts();
TaskCompletionSource<string> tcs = new TaskCompletionSource<string>();
cons.SearchCompleted += (sender,args) =>
{
tcs.TrySetResult(ExtractWhatIWantFromResults(args.Results));
};
cons.SearchAsync(String.Empty, FilterKind.None, "My Contact");
return tcs.Task;
}
TaskCompletionSource is the common way to use.
Not tested (No idea how to test without knowing your classes/methods),
Task<string> MyMethodAsync()
{
Contacts cons = new Contacts();
TaskCompletionSource<string> tcs = new TaskCompletionSource<string>();
cons.SearchCompleted += (sender,args) =>
{
tcs.TrySetResult(args.Results);
};
cons.SearchAsync(String.Empty, FilterKind.None, "My Contact");
return tcs.Task;
}
To bridge EAP and TAP, you should use TaskCompletionSource, as such:
public static Task<IEnumerable<Contact>> SearchTaskAsync(this Contacts contacts, string filter, FilterKind filterKind)
{
var tcs = new TaskCompletionSource<IEnumerable<Contact>>();
EventHandler<ContactsSearchEventArgs> subscription = null;
subscription = (_, e) =>
{
contacts.SearchCompleted -= subscription;
tcs.TrySetResult(e.Results);
};
contacts.SearchCompleted += subscription;
contacts.SearchAsync(filter, filterKind, null);
return tcs.Task;
}
which you can use like this:
async Task<string> MyMethodAsync()
{
Contacts cons = new Contacts();
var searchResults = await cons.SearchTaskAsync(String.Empty, FilterKind.None);
string result = SomeOtherSynchronousOperation(searchResults);
await DoOtherStuffWithResult(result);
return result;
}
In fact, the MSDN docs for TAP are really of an unusually high quality and I strongly recommend reading through that entire section.
Do the Windows synchronization primitives still have a place in an async/await world?
Not so much, because as soon as you block a thread, you lose the benefits of asynchronous code. That said, you can emulate similar behavior using TAP-based primitives; Stephen Toub has a series of blog entries that explore this and I implemented similar primitives in my AsyncEx library.

Categories