About asynchronous tasks - c#

I have a form with a button and a list box. I want to add to the list box the results from two functions. These two functions can take an unknown amount of time to complete and I want to execute them simultaneously. As soon as either of the functions completes the computation, I want to display the result in the list box (before the other function completed). At the moment, the results are displayed after both functions complete. I wouldn't mind if the functions update the list box themselves.
async Task<string> LongTaskAsync()
{
for(int i = 0; i < 50; i++) {
Thread.Sleep(100);
}
return "Completed long task async";
}
async Task<string> ShortTaskAsync()
{
for(int i = 0; i < 5; i++) {
Thread.Sleep(100);
}
return "Completed short task async";
}
async void BtnRunClick(object sender, EventArgs e)
{
listBox1.Items.Clear();
var longTask = Task.Run(() => LongTaskAsync());
var shortTask = Task.Run(() => ShortTaskAsync());
listBox1.Items.Add(await longTask);
listBox1.Items.Add(await shortTask);
}

The reason why it shows 2 of them at the same time related how you chain your awaits.
listBox1.Items.Add(await longTask);
listBox1.Items.Add(await shortTask);
You are awaiting longer task before the shorter one. The second line runs after long task done its work in this time shorter one was already completed that's why you see them at the same time. But in a world you don't know what task will take longer to execute you need to have a better solution.
Action<Task<string>> continuationFunction = t => { this.listBox1.Items.Add(t.Result); };
Task.Run(() => LongTaskAsync()).ContinueWith(continuationFunction, TaskScheduler.FromCurrentSynchronizationContext());
Task.Run(() => ShortTaskAsync()).ContinueWith(continuationFunction, TaskScheduler.FromCurrentSynchronizationContext());
TaskScheduler.FromCurrentSynchronizationContext() is for to avoid cross thread access exceptions.

You don't have to use ContinueWith for this. It's almost always possible to avoid mixing async/await and ContinueWith-style of continuations. In your case, it can be done like this:
async void BtnRunClick(object sender, EventArgs e)
{
listBox1.Items.Clear();
async Task longTaskHelperAsync() {
// probably, Task.Run is redundant here,
// could just do: var item = await LongTaskAsync();
var item = await Task.Run(() => LongTaskAsync());
listBox1.Items.Add(item);
}
async Task shortTaskHelperAsync() {
// probably, Task.Run is redundant here, too
var item = await Task.Run(() => ShortTaskAsync());
listBox1.Items.Add(item);
}
await Task.WhenAll(longTaskHelperAsync(), shortTaskHelperAsync());
}
I believe this way it's more readable and you don't have to worry about synchronization context, FromCurrentSynchronizationContext, etc.
Also, most likely you'd want to take care of possible re-entrancy, if BtnRunClickis clicked again while those async ctasks are still in-flight.

You could solve it a bit more generically by creating a method that awaits a task, and also adds the result of the task to the ListBox.
async Task ProcessAndAddToListAsync(Func<Task<string>> function)
{
var value = await Task.Run(function); // Start the task in a background thread
listBox1.Items.Add(value); // Update the control in the UI thread
}
Then use this method inside the event handler of the button-click event:
async void BtnRunClick(object sender, EventArgs e)
{
listBox1.Items.Clear();
var longTask = ProcessAndAddToListAsync(LongTaskAsync);
var shortTask = ProcessAndAddToListAsync(ShortTaskAsync);
await Task.WhenAll(longTask, shortTask); // optional
// Here do anything that depends on both tasks being completed
}

Related

Using a BlockingCollection to queue Tasks

I am trying to create a way to queue up Tasks to run, so I have tried to implement it using a BlockingCollection. The problem I find is whenever I try to add the Task, the Task executes. Sample code as below:
private void button1_Click(object sender, EventArgs e)
{
textBox2.Clear();
for (int i = 0; i < 10; i++)
_processCollection.Add(BigTask(i));
}
static BlockingCollection<Task> _processCollection = new BlockingCollection<Task>();
Thread ConsumerThread = new Thread(LaunchConsumer);
private static async void LaunchConsumer()
{
while (true)
{
var processTask = _processCollection.Take();
await Task.Run(() => processTask);
}
}
async Task BigTask(int i)
{
await Task.Delay(5000);
textBox2.AppendText($"Text{i}\n");
}
What seems to happen in debug is all the tasks seem to run as they are added into the blocking collection. I tried switching the blocking collection to use Action, but that just leads to nothing happening. As below (only changes shown):
private void button1_Click(object sender, EventArgs e)
{
textBox2.Clear();
for (int i = 0; i < 10; i++)
{
int iC = i;
_processCollection.Add(async () => await BigTask(iC));
}
}
static BlockingCollection<Action> _processCollection = new BlockingCollection<Action>();
Thread ConsumerThread = new Thread(LaunchConsumer);
private static async void LaunchConsumer()
{
while (true)
{
var processTask = _processCollection.Take();
await Task.Run(processTask);
}
}
I feel like I have made some small error somewhere, because it feels like this should work. I have tried to find someone doing something similar but have had no luck, which makes me think maybe my concept is flawed so feel free to suggest an alternative.
_processCollection.Add(BigTask(i)); doesn't work because this calls BigTask(i) immediately, and when that is called, the work starts.
You were on the right track by wrapping this in a separate BigTask launcher, but by using Action, you don't provide your LaunchConsumer with any means to track the progress. await Task.Run(processTask) will continue pretty much immediately with the next task. You need to use Func<Task> to avoid that.
The reason you don't see any results is likely unrelated. Now that you manage to launch the task from your newly created thread, the call to textBox2.AppendText is no longer done from the UI thread. That's not supported. Only the UI thread can access UI objects. You can use textBox2.Invoke to pass an action back to the UI thread, and that action can then call AppendText.
Tested working code:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
ConsumerThread.Start();
}
private void button1_Click(object sender, EventArgs e)
{
textBox2.Clear();
foreach (var i in Enumerable.Range(0, 10))
_processCollection.Add(() => BigTask(i));
}
static BlockingCollection<Func<Task>> _processCollection = new BlockingCollection<Func<Task>>();
Thread ConsumerThread = new Thread(LaunchConsumer);
private static async void LaunchConsumer()
{
while (true)
{
var processTask = _processCollection.Take();
await Task.Run(processTask);
}
}
async Task BigTask(int i)
{
await Task.Delay(5000);
textBox2.Invoke(new Action(() => textBox2.AppendText($"Text{i}\n")));
}
}
That said, BlockingCollection is not really the best collection type to use here. It dedicates one thread to pretty much nothing but waiting. Also, Task.Run when you're already in a background thread can admittedly sometimes be useful, but doesn't add anything here. What to do instead depends on your needs. Whether all tasks are known beforehand makes a difference. Whether you may want multiple consumers makes a difference. Other things I haven't thought of may also make a difference.

c# wait for a task inside a loop

I'm using TPL in a Windows Form Application in the following way:
foreach (var item in items)
{
task = Task<object>.Factory.StartNew (() => doWork());
task.Wait();
//update the UI using the result
}
I am waiting for the task to finish, because I need to process every item in the list, but as you imagine this is causing a lock in my UI thread (the UI freezes).
I'd like to know how can I implement this in a way that does not lock the UI thread.
Update: I'm using .NET Framework 4.5
Thanks.
P.S DoWork() is a long run operation.
You can go about this in a couple of ways.
One would be not to execute each item in your list on a thread pool thread, but queue the entire foreach loop inside a Task and then update the results when they finish.
public async void SoneEventHandler(object sender, EventArgs e)
{
var result = await Task.Run(() => items.Select(item => DoWork()).ToList());
foreach (var item in items)
{
// Update UI
}
}
If you still want to process each item on a threadpool threas, use Task.Run and await on the Task, don't block with Task.Wait():
public async void SoneEventHandler(object sender, EventArgs e)
{
foreach (var item in items)
{
var result = await Task.Run(() => DoWork());
// Update UI
}
}
Make your event handler async, and call await on the task representing the thread pool operation. This will cause your event handler to relinquish control over the UI thread until the task completes, then resume to update the UI and start the next task.
private async void MyButton_Click(object sender, EventArgs e)
{
foreach (var item in items)
{
// assuming C# 5 closure semantics
await Task.Run(() => doWork(item));
// update the UI using the result
}
}
The above approach assumes that you want to run your tasks sequentially. If you want to launch them in parallel and process their results in order of completion, you could use:
private async void MyButton_Click(object sender, EventArgs e)
{
var tasks = items.Select(item => Task.Run(() => doWork(item))).ToList();
while (tasks.Any())
{
var task = await Task.WhenAny(tasks);
tasks.Remove(task);
var result = await task;
// update the UI using the result
}
}

How can I create call an async method inside another async method without it stalling

I need to call multiple async methods and inside of them, call another async method aswell.
Let me demonstrate
private async void button1_Click(object sender, EventArgs e)
{
for(int i = 0; i< 100; i++)
{
await Method1();
}
}
public async Task Method1()
{
await Task.Delay(3*1000);
await Method2();
}
public async Task Method2()
{
await Task.Delay(10*1000);
}
My problem is, the for statement only activates the iterations after the wait on Method2 starts and what I want is to create the 100 Task all at once. Everything else will be done asynchronously.
I think you are confused as to what "await" means. "await" means "start processing this thing asynchronously, and while that is ticking away, go back to the windows message loop and keep on processing messages so that the UI keeps on re-drawing. When the asynchronous task is done, pick up the code where I awaited".
So when you await in a loop like that, you are saying:
Start the first asynchronous job...
and while it is running, keep processing the message loop.
When the first asynchronous job is done...
pick up in the loop where we awaited. We go around the loop and...
Start the second asynchronous job...
If that's not what you want then don't await the task. If you want to start a hundred tasks that run concurrently then start a hundred tasks and don't await any of them. Why is there any await in the click handler?
Rather than await-ing each tasks as you create it in the for loop, you just want to start all of the tasks. You don't want to delay the scheduling of the next task until the previous finished, so just don't await:
private void button1_Click(object sender, EventArgs e)
{
for(int i = 0; i< 100; i++)
{
var task = Method1();
}
}
Done.
If it's important that you do something after all of the tasks finish, while still doing all of them in parallel, then you can rely on Task.WhenAll to generate a task that will be completed when all of the other tasks are done:
private async void button1_Click(object sender, EventArgs e)
{
var tasks = new List<Task>();
for(int i = 0; i< 100; i++)
{
tasks.Add(Method1());
}
await Task.WhenAll(tasks);
textbox1.Text = "Done!"; //or whatever you want to do when they're all done
}

How do I force a task to run on the UI thread?

Original message below. Let me try and explain with more details why I am asking for this.
I have a page that listens to the Share charm request:
void Page_Loaded(object sender, RoutedEventArgs e)
{
m_transferManager = Windows.ApplicationModel.DataTransfer.DataTransferManager.GetForCurrentView();
m_transferManager.DataRequested += TransferManager_DataRequested;
}
When the event fires (TransferManager_DataRequested) it does not fire on the UI thread:
void TransferManager_DataRequested(DataTransferManager sender, DataRequestedEventArgs args)
{
var data = args.Request.Data;
// More related stuff omitted - not important.
data.SetDataProvider(StandardDataFormats.Bitmap, GetImage_DelayRenderer);
}
Now, when GetImage_DelayRender is called, it also does not get called on the UI thread. However, in it, I need to do a bunch of UI related things. Specifically, I need to call a method that only works on the UI (it's a method I use elsewhere and I want to reuse it's logic). The method is called GetImageAsync and it needs to run on the UI because it does a bunch of interactions with WriteableBitmap. It also does a bunch of async operations (such as writing to stream etc) which is why it's async. I block the UI on GetImageAsync() for as short a time as I can.
Here's what GetImage_DelayRender looks like:
private async void GetImage_DelayRenderer(DataProviderRequest request)
{
var deferral = request.GetDeferral();
await Dispatcher.RunTask(async () => // RunTask() is an extension method - described in the original question below.
{
try
{
var bitmapStream = await GetImageAsync();
request.SetData(RandomAccessStreamReference.CreateFromStream(bitmapStream));
}
catch
{
}
});
deferral.Complete();
}
What I want to know is, what is the most correct way to achieve the call to Dispatcher.RunTask() above (which is my hack extension method).
----- START ORIGINAL MESSAGE -------
Say I have the following task:
private async Task SomeTask()
{
await Task.Delay(1000);
// Do some UI and other stuff that may also be async
}
Edit (Clarification): I do not want to block the UI. The task I want to execute (even in the example, if you read it) WILL NOT block the UI. I just want the task to run in the context of the UI for it's synchronous portions.
I want to run this on code on the UI thread as an Async operation. Dispatcher.RunXXX() methods take an action, which means they will run the action and notify you when they are done. That's not good enough. I need the entire task to run on the UI thread (as it would have executed had I run it from the UI thread) and then, when done, to notify me back.
The only way I could think of, is to use the Dispatcher.RunXXX() methods to execute an anon delegate that sets a local variable in my method to the task and then awaits that...
public async static Task RunTask(this CoreDispatcher dispatcher, Func<Task> taskGiver)
{
Task task = null;
await dispatcher.RunAsync(() => task = taskGiver());
await task;
}
This looks pretty damn ugly. Is there a better way of doing it?
Edit2: Guys - read this code - if I execute the first code block above using the RunTask() hack I have, IT WILL NOT BLOCK THE UI on the Task.Delay()...
I want to run this on code on the UI thread as an Async operation.
Then just run it:
async void MyEventHandler(object sender, ...)
{
await SomeTask();
}
Update:
I'm not sure this is a "legal" operation, but you can schedule that method to run on the UI thread by capturing the CoreDispatcher while the UI is active and later calling RunAsync:
private async void GetImage_DelayRenderer(DataProviderRequest request)
{
var deferral = request.GetDeferral();
Task task = null;
await coreDispatcher.RunAsync(() => { task = SomeTask(); });
await task;
deferral.Complete();
}
I don't have time to do a complete solution, so hopefully you will still find this useful...
First, as others have pointed out, you cannot run something on the UI thread and not have it block the UI thread. End of discussion. What you are saying you need is something to run on a non-UI thread and periodically notify the UI thread that there are updates that need to be processed.
To accomplish this, you need something like this...
public class LongTask
{
public event EventHandler MyEvent;
public void Execute()
{
var task = Task.Factory.StartNew(() =>
{
while (true)
{
// condition met to notify UI
if (MyEvent != null)
MyEvent(this, null);
}
});
}
}
In your UI then, do something like...
private void button_Click(object sender, RoutedEventArgs e)
{
var test = new LongTask();
test.MyEvent += test_MyEvent;
test.Execute();
}
void test_MyEvent(object sender, EventArgs e)
{
Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
{
test.Text += " bang ";
});
You could obviously implement this in a much cleaner fashion using something like MVVM, but this is the basic idea.
}
I've done it like this:
public static Task<string> GetResultAsync()
{
return Task<string>.Factory.StartNew(() => GetResultSync());
}
In UI:
private async void test()
{
string result = await GetResultAsync();
// update UI no problem
textbox.Text = result;
}

When to use TaskEx.Run vs. TaskEx.RunEx

I'm trying to understand when to use TaskEx.Run. I have provided two code sample i wrote below that produce the same result. What i fail to see is why i would take the Task.RunEx TaskEx.RunEx approach, I'm sure there is a good reason and was hoping someone could fill me in.
async Task DoWork(CancellationToken cancelToken, IProgress<string> progress)
{
int i = 0;
TaskEx.RunEx(async () =>
{
while (!cancelToken.IsCancellationRequested)
{
progress.Report(i++.ToString());
await TaskEx.Delay(1, cancelToken);
}
}, cancelToken);
}
private void Button_Click(object sender, RoutedEventArgs e)
{
if (button.Content.ToString() == "Start")
{
button.Content = "Stop";
cts.Dispose();
cts = new CancellationTokenSource();
listBox.Items.Clear();
IProgress<string> progress = new Progress<string>(s =>
{
listBox.Items.Add(s);
listBox.ScrollIntoView(listBox.Items[listBox.Items.Count - 1]);
});
DoWork(cts.Token, progress);
}
else
{
button.Content = "Start";
cts.Cancel();
}
}
I can achieve the same results like so
async Task DoWork(CancellationToken cancelToken)
{
int i = 0;
while (!cancelToken.IsCancellationRequested)
{
listBox.Items.Add(i++);
listBox.ScrollIntoView(listBox.Items[listBox.Items.Count - 1]);
await TaskEx.Delay(100, cancelToken);
}
}
private void Button_Click(object sender, RoutedEventArgs e)
{
if (button.Content.ToString() == "Start")
{
button.Content = "Stop";
cts.Dispose();
cts = new CancellationTokenSource();
listBox.Items.Clear();
DoWork(cts.Token);
}
else
{
button.Content = "Start";
cts.Cancel();
}
}
Use TaskEx.Run when you want to run synchronous code in a thread pool context.
Use TaskEx.RunEx when you want to run asynchronous code in a thread pool context.
Stephen Toub has two blog posts related to the difference in behavior:
Potential pitfalls to avoid when passing around async lambdas
Task.Run vs Task.Factory.StartNew
This is only one of several options you have for creating tasks. If you do not have to use Run/RunEx, then you should not. Use simple async methods, and only use Run/RunEx if you need to run something in the background.
The difference between your two DoWork() methods is that the first one (that uses TaskEx.RunEx()) is not asynchronous at all. It executes fully synchronously, starts the other task on another thread, and immediately returns a completed Task. If you awaited or Wait()ed on that task, it wouldn't wait until the internal task is completed.
Task.Run spawns a new thread in most scenarios as I understand it.
It's important to note that simply because you mark a method as async, and use awaiters, this does NOT (necessarily) mean that new threads are being created, completions are scheduled on the SAME thread of execution that they were called from in many cases.
The trick here has to do with the SchedulingContext. If it's set for a multithreaded apartment, then you're going to delegate completions to viable threads on the threadpool. If you're in a singlethreaded apartment as all WPF and WinForms UI code is, then it will return to the calling thread for completion allowing work to be done directly on the UI without visible thread marshalling in the code.

Categories