I'm new to async / await, and have been trying to implement it in my 4.6 web api 2 project.
public class MyController : ApiController
{
public async Task<Thing> Search(String searchTerms)
{
myThing = new Thing();
myThing.FirstProperty = await doFirstPropertyAsync(searchTerms);
myThing.SecondProperty = await doSecondPropertyAsync(searchTerms);
return myThing;
}
}
Basically I'm returning a class (Thing) that has two properties that take a few seconds each to populate. I'm actually loading maybe ~10 properties, but it's the same logic for all of them.
public async Task<MyCoolSubObject> doFirstPropertyAsync(string searchTerms)
{
SomeController sController = new SomeController();
Debug.WriteLine("first - starting.");
var x = await Task.Run(()=>sController.Lookup(searchTerms));
Debug.WriteLine("first - finishing.");
return x;
}
public async Task<MyCoolSubObject> doSecondPropertyAsync(string searchTerms)
{
SomeOtherController sController = new SomeOtherController();
Debug.WriteLine("second - starting.");
var x = await Task.Run(()=>sController.Lookup(searchTerms));
Debug.WriteLine("second - finishing.");
return x;
}
What's got my scratching my head:
When I look at the debug outputs, the first property assignment method call starts and finishes before the second completes. Again, I actually have like ten of these and no matter what order I put the property assignments in they complete in a serial fashion (ie: nothing starts until another one finishes).
These property assignments under the hood are basically doing database calls that take a while, hence I wanted them running in parallel if possible. The methods themselves ( SomeController.Lookup(string) ) contain no await/async/task stuff.
Again, I actually have like ten of these and no matter what order I
put the property assignments in they complete in a serial fashion (ie:
nothing starts until another one finishes).
This happens because in your code you use the await keyword as soon as you kickoff the task, by doing that you prevent the method to continue to execute the next statement before the task will be done.
If you want to run your tasks in parallel you should kickoff all of them and only then await all of them using Task.WhenAll:
public async Task<Thing> Search(String searchTerms)
{
myThing = new Thing();
var firstTask = doFirstPropertyAsync(searchTerms);
var secondTask = doSecondPropertyAsync(searchTerms);
await Task.WhenAll(firstTask, secondTask);
myThing.FirstProperty = await firstTask;
myThing.SecondProperty = await secondTask;
return myThing;
}
Note that when we await every task separately after we await Task.WhenAll the tasks have already been done, we do that in order to get the result from the task, although we can use the Result property (it will not block since we know the task has already been done) I prefer to use await for consistency reasons.
Related
I am new to Blazor. I'm working on a Server App (not WASM).
On a page load, I am loading 2 things to a page. Item1 is loaded at the same time as Item2 via API calls. I'd like individual items to show up as soon as they are available. I was thinking something like
protected override async Task OnInitializedAsync()
{
var t1 = _service.GetItem1().ContinueWith(t => {
Item1Loaded = true;
Item2State = t.Result;
await InvokeAsync(StateHasChanged);
});
var t2 = _service.GetItem2().ContinueWith(t => {
Item2Loaded = true;
Item2State = t.Result;
await InvokeAsync(StateHasChanged);
});
}
I have a couple questions about this though:
Do I need to worry about canceling these lines if the user navigates away from the component? (would changing a state variable after the component is removed cause a problem) or does Blazor handle that at the framework level somehow?
Do I need to ensure this gets back to a certain thread with a Synchronization Context? It seems like InvokeAsync just does this for me but want to be sure.
Its hard to find lots of modern examples of ContinueWith. async/await is dominant, but I don't think it allows continuations to execute in the order they complete. Is this a reasonable use of it?
Since you are using Server side you can do this more cleanly (ContinueWith is more or less obsolete since async / await):
protected override async Task OnInitializedAsync()
{
var t1 = Task.Run(async () =>
{ Item1State = await _service.GetItem1();
Item1Loaded = true; // you can probably derive this from Item1State
await InvokeAsync(StateHasChanged);
});
var t2 = Task.Run(async () =>
{ Item2State = await _service.GetItem2();
Item2Loaded = true;
await InvokeAsync(StateHasChanged);
});
await Task.WhenAll(t1, t2);
}
No need to call StateHasChanged() here.
Without the ItemLoaded guards you could do this without Task.Run().
Do I need to worry about canceling these lines if the user navigates away
Most modern DB stuff can be passed a cancellation token. So, use that if you wish to cancel the operation. If It's your own code and the operations are long running, consider using cancellation tokens.
Do I need to ensure this gets back to a certain thread with a Synchronization Context?
Calling await InvokeAsync(StateHasChanged); ensures the UI code is run on the UI thread.
Question 3
Stick with simple await. Yes, it executes in order. And call await InvokeAsync(StateHasChanged) between operations to update the UI if you have more than two await operations.
Note : This will only work if you await real async code, not sync code wrapped in a Task! If there are no yields, the Renderer gets no thread time so the UI doesn't get updated till it gets some.
I'm trying to figure out how to refresh the client-side component after button click.
Repo Link with example: https://github.com/ovie91/RefreshComponent
Site /test or from nav menu test
So I have OnInitializedAsync method that is retrieving data from API
protected override async Task OnInitializedAsync()
{
result = await (some API Call);
}
Then I have a method connected to the button
private async void ButtonClick()
{
await (some API Call);
result = null;
this.StateHasChanged(); <--- Doesnt work :<
}
I have tried to use this.StateHasChanged(); but there is no reaction.
As a workaround, I can force you to navigate again to the same website but this refresh "Whole" website but not a component.
Any ideas on how to deal with it?
whole code (stripped to minimum):
#page "/test"
#inject HttpClient Http
#if (result == null)
{
<p>Loading...<p>
}
else
{
#result
<button #onclick="(() => ButtonClick())">Click</button>
}
#code {
private APIObject result;
protected override async Task OnInitializedAsync()
{
result = await (some API Call);
}
private async void ButtonClick()
{
await (some API Call);
result = null;
this.StateHasChanged(); <--- Doesnt work :<
}
}
Update
I want to refresh component so OnInitializedAsync would be triggered again and that would mean I don't have to run the same code again after button click. Hope you understand what I mean.
To get the desired output you just have to shuffle the lines a little, from:
private async void ButtonClick()
{
await (some API Call); // UI checks if an update is needed (No)
result = null; // now an update is needed
this.StateHasChanged(); <--- Doesnt work :< // actually: not needed
}
to:
private async Task ButtonClick()
{
result = null; // change the state
//this.StateHasChanged(); // not needed, a request is pending
await (some API Call); // should show '<h3>Loading</h3>' now
}
Note that the UI is updated when an await releases the Thread.
however, from your answer we get
var APICall = await Http.GetAsync("SomeAPI");
Thread.Sleep(2000);
This should work when Http.GetAsync("SomeAPI"); really is an async call and not just some stand-in pseudo code. Because Thread.Sleep(2000); will really freeze things.
If you want to make sure:
private async Task GetData()
{
await Task.Delay(1); // release the thread for rendering
var APICall = await Http.GetAsync("SomeAPI");
Random rnd = new Random();
Thread.Sleep(2000); // Task.Delay() is much preferred
result = "Random Number: " + rnd.Next();
}
Thread.Sleep() is appropriate to simulate some CPU (not I/O) intensive code. So I'm not saying it's wrong but be aware of the difference.
And it is much better to make eventhandlers async Task instead of async void but that is not the direct problem here.
From here:
Blazor uses a synchronization context (SynchronizationContext) to enforce a single logical thread of execution. A component's lifecycle methods and any event callbacks that are raised by Blazor are executed on the synchronization context.
Blazor Server's synchronization context attempts to emulate a single-threaded environment so that it closely matches the WebAssembly model in the browser, which is single threaded. At any given point in time, work is performed on exactly one thread, giving the impression of a single logical thread. No two operations execute concurrently.
So as enet asnwered, you should use async Task signature instead of async void.
I have moved API call to another Method and inside of OnInitializedAsync I called it.
Then when I reset the result variable to see Loading state I'm able to "refresh" component to achieve that you need to add. this.StateHasChanged()
Now I have a responsive component to updates that are happening :)
#page "/test"
#using System.Threading;
#inject HttpClient Http
#if (result == null)
{
<h3>Loading</h3>
}
else
{
#result
<button #onclick="(() => ButtonClick())">Click</button>
}
#code {
private string result;
protected override async Task OnInitializedAsync()
{
await GetData();
}
private async Task GetData()
{
var APICall = await Http.GetAsync("SomeAPI");
Random rnd = new Random();
Thread.Sleep(2000);
result = "Random Number: " + rnd.Next();
}
private async Task ButtonClick()
{
await Http.GetAsync("SomeAPIcall");
result = null; // required to see loading state.
this.StateHasChanged(); // when added model is refreshed and Loading state is visible.
await GetData();
}
}
After reading pretty much every question on Stack Overflow and Microsoft's documentation about NetworkStream, I dont understand what is wrong with my code.
The problem I see is that my method GetDataAsync() hangs very often. I call this method from Init Method like so:
public MyView(string id)
{
InitializeComponent();
MyViewModel myViewModel = session.Resolve<MyViewModel>(); //Autofac
myiewModel.Init(id);
BindingContext = myViewModel;
}
Above, my View does its initialization, then resolves MyViewModel from Autofac DiC and then calls MyViewModel Init() method to do some additional setup on the VM.
The Init method then calls my Async method GetDataAsync which return a IList like so:
public void Init()
{
// call this Async method to populate a ListView
foreach (var model in GetDataAsync("111").Result)
{
// The List<MyModel> returned by the GetDataAsync is then
// used to load ListView's ObservableCollection<MyModel>
// This ObservableCollection is data-bound to a ListView in
// this View. So, the ListView shows its data once the View
// displays.
}
}
, and here is my GetDataAsync() method including my comments:
public override async Task<IList<MyModel>> GetDataAsync(string id)
{
var timeout = TimeSpan.FromSeconds(20);
try
{
byte[] messageBytes = GetMessageBytes(Id);
using (var cts = new CancellationTokenSource(timeout))
using (TcpClient client = new TcpClient(Ip, Port))
using (NetworkStream stream = client.GetStream())
{
await stream.WriteAsync(messageBytes, 0, messageBytes.Length, cts.Token);
await stream.FlushAsync(cts.Token);
byte[] buffer = new byte[1024];
StringBuilder builder = new StringBuilder();
int bytesRead = 0;
await Task.Delay(500);
while (stream.DataAvailable) // need to Delay to wait for data to be available
{
bytesRead = await stream.ReadAsync(buffer, 0, buffer.Length, cts.Token);
builder.AppendFormat("{0}", Encoding.ASCII.GetString(buffer, 0, bytesRead));
}
string msg = buffer.ToString();
}
return ParseMessageIntoList(msg); // parses message into IList<MyModel>
}
catch (OperationCanceledException oce)
{
return await Task.FromResult<IList<RoomGuestModel>>(new List<RoomGuestModel>());
}
catch (Exception ex)
{
return await Task.FromResult<IList<RoomGuestModel>>(new List<RoomGuestModel>());
}
}
I would expect that a ReadAsync or WriteAsync either complete successfully, throw some exception, or get cancelled after 10 seconds in which case I would catch OperationCanceledException.
However, it just hangs endlessly when I call method above. If I am debugging and have some breakpoints in the code above, I will be able to go through the method entirely but if I call it 2nd time, app just hangs forever.
I am new to Tasks and Async programming, so I am also not sure I do my cancellations and exception handling properly here?
UPDATE AND FIX
I figured out how to fix the deadlock issue. In hope this will help others sho might run into the same issue, I'll first explain it. The articles that helped me a lot are:
https://devblogs.microsoft.com/pfxteam/await-and-ui-and-deadlocks-oh-my/ by Stephen Taub
https://montemagno.com/c-sharp-developers-stop-calling-dot-result/ by James Montemagno
https://msdn.microsoft.com/en-us/magazine/jj991977.aspx by StephenCleary
https://blog.xamarin.com/getting-started-with-async-await/ by Jon Goldberger
#StephenCleary was great help understanding the issue. Calling Result or Wait (above, I call Result when calling GetDataAsync) will lead to dead-lock.
The context thread (UI in this case) is now waiting for GetDataAsync to complete, but GetDataAsync captures the current context-thread (UI thread), so it can resume on it once it gets data from TCP. But since this context-thread is now blocked by call to Result, it cannot resume.
The end result is that it looks like call to GetDataAsync has deadlocked but in reality, it is call to Result that deadlocked.
After reading tons of articles from #StephenTaub, #StephenCleary, #JamesMontemagno, #JoeGoldenberger (thank you all), I started getting understanding of the issue (I am new to TAP/async/await).
Then I discovered continuations in Tasks and how to use them to resolve the issue (thanks to Stephen Taub's article above).
So, instead of calling it like:
IList<MyModel> models = GetDataAsync("111").Result;
foeach(var model in models)
{
MyModelsObservableCollection.Add(model);
}
, I call it with continuation like this:
GetDataAsync(id)
.ContinueWith((antecedant) =>
{
foreach(var model in antecedant.Result)
{
MyModelsObservableCollection.Add(model);
}
}, TaskContinuationOptions.OnlyOnRanToCompletion)
.ContinueWith((antecedant) =>
{
var error = antecedant.Exception.Flatten();
}, TaskContinuationOptions.OnlyOnFaulted);
This seam to have fixed my deadlocking issue and now my list will load fine even though it is loaded from the constructor.
So, this seam to work just fine. But #JoeGoldenberger also suggests another solution in his article https://blog.xamarin.com/getting-started-with-async-await/ which is to use Task.Run(async()=>{...}); and inside that await GetDataAsync and load ObservableCollection. So, I gave that a try as well and that is not blocking either, so working great:
Task.Run(async() =>
{
IList<MyModel> models = await GetDataAsync(id);
foreach (var model in models)
{
MyModelsObservableCollection.Add(model);
}
});
So, it looks like either of these 2 will remove deadlock just fine. And since above my Init method is called from a c-tor; therefore, I cannot make it Async and await on this, using one of the 2 methods described above resolves my problem. I dont know which one is better but in my tests, they do work.
Your problem is most likely due to GetDataAsync("111").Result. You shouldn't block on async code.
This can cause deadocks. E.g., if you're on a UI thread, the UI thread will start GetDataAsync and run it until it hits an await. At this point, GetDataAsync returns an incomplete task, and the .Result call blocks the UI thread until that task is completed.
Eventually, the inner async call completes and GetDataAsync is ready to resume executing after its await. By default, await captures its context and resumes on that context. Which in this example is the UI thread. Which is blocked since it called Result. So, the UI thread is waiting for GetDataAsync to complete, and GetDataAsync is waiting for the UI thread so it can complete: deadlock.
The proper solution is to go async all the way; replace .Result with await, and make the necessary changes to other code for that to happen.
As stated in my update, going async all the way by providing an async lambda like below resolved the issue for me
Task.Run(async() =>
{
IList<MyModel> models = await GetDataAsync(id);
foreach (var model in models)
{
MyModelsObservableCollection.Add(model);
}
});
Loading asynchronously an observable collection in a ctor this way (in my case, ctor calls Init which then uses this Task.Run) solves problem
In one documentation they say IHandleMessages handler hast to be written this way (signature is automatically generated when I choose to "Implement interface" option in Visual Studio):
public class PlaceOrderHandler : IHandleMessages<PlaceOrder>
{
public Task Handle(PlaceOrder message, IMessageHandlerContext context)
{
var orderPlaced = new OrderPlaced { OrderId = message.OrderId };
return context.Publish(orderPlaced);
}
}
While another documentation says it has to be written this way:
public class PlaceOrderHandler : IHandleMessages<PlaceOrder>
{
public async Task Handle(PlaceOrder message, IMessageHandlerContext context)
{
var orderPlaced = new OrderPlaced { OrderId = message.OrderId };
await context.Publish<OrderPlaced>(e => { e.OrderId = message.OrderId; });
}
}
I wonder what is the difference between these two statements, can someone explain in simple language?
Which option is the right one?
Both are correct options. The difference between the two is how a single asynchronous operation is handles in the Handle method.
In the first case, a Task is returned as-is. In the second case, publishing is awaited within the Handle method. The difference? In the first case no async state machine is created by the compiler as the task of publishing returned back. In the second scenario, a state machine is created.
Which option is the right one to use? They are both correct options. If a method is called frequently and you care for the unnecessary allocations not to take place, returnng a single task without awaiting is more efficient.
How do I merge a List<T> of TPL-based tasks for later execution?
public async IEnumerable<Task<string>> CreateTasks(){ /* stuff*/ }
My assumption is .Concat() ...
void MainTestApp() // Full sample available upon request.
{
List<string> nothingList = new List<string>();
nothingList.Add("whatever");
cts = new CancellationTokenSource();
delayedExecution =
from str in nothingList
select AccessTheWebAsync("", cts.Token);
delayedExecution2 =
from str in nothingList
select AccessTheWebAsync("1", cts.Token);
delayedExecution = delayedExecution.Concat(delayedExecution2);
}
/// SNIP
async Task AccessTheWebAsync(string nothing, CancellationToken ct)
{
// return a Task
}
I want to make sure that this won't spawn any task or evaluate anything. In fact, I suppose I'm asking "what logically executes an IQueryable to something that returns data"?
Background
Since I'm doing recursion and I don't want to execute this until the correct time, what is the correct way to merge the results if called multiple times?
If it matters I'm thinking of running this command to launch all the tasks var AllRunningDataTasks = results.ToList(); followed by this code:
while (AllRunningDataTasks.Count > 0)
{
// Identify the first task that completes.
Task<TableResult> firstFinishedTask = await Task.WhenAny(AllRunningDataTasks);
// ***Remove the selected task from the list so that you don't
// process it more than once.
AllRunningDataTasks.Remove(firstFinishedTask);
// TODO: Await the completed task.
var taskOfTableResult = await firstFinishedTask;
// Todo: (doen't work)
TrustState thisState = (TrustState)firstFinishedTask.AsyncState;
// TODO: Update the concurrent dictionary with data
// thisState.QueryStartPoint + thisState.ThingToSearchFor
Interlocked.Decrement(ref thisState.RunningDirectQueries);
Interlocked.Increment(ref thisState.CompletedDirectQueries);
if (thisState.RunningDirectQueries == 0)
{
thisState.TimeCompleted = DateTime.UtcNow;
}
}
To answer the specific question "what logically executes an IQueryable to something that returns data"? That would be anything that either forces the production of at least one value, or forces the discovery of whether a value is available.
For example, ToList, ToArray, First, Single, SingleOrDefault, and Count will all force evaluation. (Although First will not evaluate the entire collection - it'll retrieve the first item and then stop.) These all have to at least start retrieving values, because there's no way for any of them to return what they return without doing so. In the case of ToList and ToArray, these return fully-populated non-lazy collections, which is why they have to evaluate everything. The methods that return a single item need to at least ask for the first item, and the Single ones will then go on to check that nothing else comes out if evaluation continues (and throw an exception if there turns out to be more).
Using foreach to iterate over the query will also force evaluation. (And again, it's for the same reason: you're asking it for actual values from the collection so it has to provide them.)
Concat does not evaluate immediately because it doesn't need to - it's only when you ask the concatenated sequence for a value that it needs to ask its inputs for values.
BTW, although you asked about IQueryable you're not using that in the examples here. This can matter, because there are some differences in how that works compared to the LINQ to Objects implementation (which you get for plain IEnumerable<T>) that you're actually getting. I don't think it makes a difference in this example, but it makes me wonder if something might have changed between your original code, and the version you posted for illustration here? It can matter because different LINQ providers can do things different ways. The IEnumerable<T> flavour of Concat definitely uses deferred evaluation, and although I'd expect that to be true for most other LINQ implementations, it's not absolutely a given.
If you need to use the results multiple times, and you want to ensure that you only evaluate them once, but that you don't evaluate them until you actually need them, then the usual approach is to call ToList at the point where you definitely need to evaluate, and then hold onto the resulting List<T> so you can use it again. Once you've got the data in List<T> (or array) form you can use that list as many times as you like.
By the way, your first question has an issue:
"How do I merge a List of TPL-based tasks for later execution?"
In general, if you already have a TPL task then you can't stop it from executing. (There is an exception to this. If you construct a Task directly instead of using one of the more normal ways of creating it, it won't actually run until you tell it to. But in general, APIs that return tasks return live ones, i.e., they may well already be running, or even complete, by the time you get your hands on them.)
The "later execution" in your example comes from the fact that you don't actually have a list of tasks at all to start with. (If you did in fact have a List<T> of tasks, "later execution" would not be an option.) What you have is a couple of enumerables which, if you were to evaluate them, would create tasks. The act of creating the task is indivisible from the act of starting it in any TAP-style API that returns a Task.
Based on the rest of what you wrote, I think what you are really asking is:
"How do I merge multiple IEnumerable<Task<T>> objects into a single IEnumerable<Task<T>> in a way that defers evaluation of the underlying enumerables until the combined enumerable itself is evaluated?"
Concat should work for that.
The following is a hacky way to get data merged... I don't like how I had to use "nothingList" in Main or a few other aspects of this sample, but it seems to get the job done and allow me to merge pending tasks.
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;
List<IEnumerable<Task>> launchList = new List<IEnumerable<Task>>();
public MainWindow()
{
InitializeComponent();
List<string> nothingList = new List<string>();
nothingList.Add("whatever");
cts = new CancellationTokenSource();
delayedExecution =
from str in nothingList
select AccessTheWebAsync("", cts.Token);
List<string> nothingList2 = new List<string>();
nothingList2.Add("whatever");
delayedExecution2 =
from str in nothingList2
select AccessTheWebAsync("1", cts.Token);
launchList.Add(delayedExecution);
launchList.Add(delayedExecution2);
delayedExecution = delayedExecution.Concat(delayedExecution2);
}
IEnumerable<Task> delayedExecution = null;
IEnumerable<Task> delayedExecution2 = null;
private async void startButton_Click(object sender, RoutedEventArgs e)
{
resultsTextBox.Clear();
// Instantiate the CancellationTokenSource.
try
{
// ***Set up the CancellationTokenSource to cancel after 25 seconds.
//cts.CancelAfter(250000);
var test = delayedExecution;// AccessTheWebAsync("", cts.Token);
var testList = test.ToList();
while (testList.Count() > 0)
{
var firstFinishedTask = await Task.WhenAny(testList);
testList.Remove(firstFinishedTask);
await firstFinishedTask;
}
resultsTextBox.Text += "\r\nDownloads complete.";
}
catch (OperationCanceledException tee)
{
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<string> AccessTheWebAsync(string nothing, CancellationToken ct)
{
// CHANGE THIS VALUE TO CONTROL THE TESTING
bool delayConversionOfQueryToList = false;
HttpClient client = new HttpClient();
// Make a list of web addresses.
List<string> urlList = null;
if (nothing == "1")
{
urlList = SetUpURLList2();
}
else 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);
// DEBUG!!!
if (delayConversionOfQueryToList == true)
{
await Task.Delay(10000);
resultsTextBox.Text += String.Format("\r\nDelay of IQueryable complete. Tip: Did you see any IsRunning messages?");
}
// ***Use ToList to execute the query and start the tasks.
List<Task<int>> downloadTasks = downloadTasksQuery.ToList();
// DEBUG!!!
if (delayConversionOfQueryToList == false)
{
await Task.Delay(10000);
resultsTextBox.Text += String.Format("\r\nDelay of .ToList() complete. Tip: Did you see any IsRunning messages?");
}
// ***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);
resultsTextBox.Text += String.Format("\r\nID {0}", firstFinishedTask.Id);
// ***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);
}
return nothing;
}
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;
}
private List<string> SetUpURLList2()
{
List<string> urls = new List<string>
{
"http://www.google.com",
};
return urls;
}
async Task<int> ProcessURL(string url, HttpClient client, CancellationToken ct)
{
resultsTextBox.Text += String.Format("\r\nIS RUNNING {0}", url);
// 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();
// Thread.Sleep(3000);
// await Task.Delay(1000, ct);
return urlContents.Length;
}
}
}
// Sample Output:
IS RUNNING http://msdn.microsoft.com
IS RUNNING http://msdn.microsoft.com/library/windows/apps/br211380.aspx
IS RUNNING http://msdn.microsoft.com/en-us/library/hh290136.aspx
IS RUNNING http://msdn.microsoft.com/en-us/library/dd470362.aspx
IS RUNNING http://msdn.microsoft.com/en-us/library/aa578028.aspx
IS RUNNING http://msdn.microsoft.com/en-us/library/ms404677.aspx
IS RUNNING http://msdn.microsoft.com/en-us/library/ff730837.aspx
IS RUNNING http://www.google.com
Delay of .ToList() complete. Tip: Did you see any IsRunning messages?
ID 1
Length of the download: 48933
ID 2
Length of the download: 375328
ID 3
Length of the download: 220428
ID 4
Length of the download: 222256
ID 5
Length of the download: 229330
ID 6
Length of the download: 136544
ID 7
Length of the download: 207171
Delay of .ToList() complete. Tip: Did you see any IsRunning messages?
ID 8
Length of the download: 43945
Downloads complete.