I have an asynchronous method DoStuffAsync that spawns two tasks with Task.Run, and both tasks report their progress using a single IProgress<int> object. From the user's perspective there is only one operation, so showing two progress bars (one for each Task) wouldn't make any sense. This is why the IProgress<int> is shared. The problem is that sometimes the UI receives the progress notifications in incorrect order. Here is my code:
private async void Button1_Click(object sender, EventArgs e)
{
TextBox1.Clear();
var progress = new Progress<int>(x => TextBox1.AppendText($"Progress: {x}\r\n"));
await DoStuffAsync(progress);
}
async Task DoStuffAsync(IProgress<int> progress)
{
int totalPercentDone = 0;
Task[] tasks = Enumerable.Range(1, 2).Select(n => Task.Run(async () =>
{
for (int i = 0; i < 5; i++)
{
await Task.Delay(100); // Simulate an I/O operation
var localPercentDone = Interlocked.Add(ref totalPercentDone, 10);
progress.Report(localPercentDone);
}
})).ToArray();
await Task.WhenAll(tasks);
}
Most of the time the notifications are in the correct order, but sometimes they are not:
This causes the ProgressBar control (not shown in the above screenshot) to jump awkwardly back and forth.
As a temporary solution I have added a lock inside the DoStuffAsync method, that includes the invocation of the IProgress.Report method:
async Task DoStuffAsync(IProgress<int> progress)
{
int totalPercentDone = 0;
object locker = new object();
Task[] tasks = Enumerable.Range(1, 2).Select(n => Task.Run(async () =>
{
for (int i = 0; i < 5; i++)
{
await Task.Delay(100); // Simulate an I/O operation
lock (locker)
{
totalPercentDone += 10;
progress.Report(totalPercentDone);
};
}
})).ToArray();
await Task.WhenAll(tasks);
}
Although this solves the problem, it causes me anxiety because I invoke arbitrary code while holding a lock. The DoStuffAsync method is actually part of a library, and could be called with a whatever IProgress<int> implementation as argument. This opens the possibility for deadlock scenarios. Is there a better way to implement the DoStuffAsync method, without using a lock, but with the desired behavior regarding the ordering of the notifications?
Your problem is that you need the increment of totalPercentDone AND the call to Report to be atomic.
There's nothing wrong with using a lock here. After all, you need some way to make the two operations atomic. If you really don't want to use lock then you could use a SemaphoireSlim:
async Task DoStuffAsync(IProgress<int> progress)
{
int totalPercentDone = 0;
var semaphore = new SemaphoreSlim(1,1);
Task[] tasks = Enumerable.Range(1, 2).Select(n => Task.Run(async () =>
{
for (int i = 0; i < 5; i++)
{
await Task.Delay(100); // Simulate an I/O operation
await semaphore.WaitAsync();
try
{
totalPercentDone += 10;
progress.Report(totalPercentDone);
}
finally
{
semaphore.Release();
}
}
})).ToArray();
await Task.WhenAll(tasks);
}
You could just report the deltas and let the handling handle them:
private async void Button1_Click(object sender, EventArgs e)
{
TextBox1.Clear();
var totalPercentDone = 0;
var progress = new Progress<int>(x =>
{
totalPercentDone += x;
TextBox1.AppendText($"Progress: {totalPercentDone}\r\n"));
}
await DoStuffAsync(progress);
}
async Task DoStuffAsync(IProgress<int> progress)
{
await Task.WhenAll(Enumerable.Range(1, 2).Select(n => Task.Run(async () =>
{
for (int i = 0; i < 5; i++)
{
await Task.Delay(100); // Simulate an I/O operation
progress.Report(10);
}
})));
}
Instead of using one int for both tasks you could use two individual ints, and take the smallest of them. Each Task need to report to 100, and not 50.
async Task DoStuffAsync(IProgress<int> progress)
{
int[] totalPercentDone = new int[2];
Task[] tasks = Enumerable.Range(1, 2).Select(n => Task.Run(async () =>
{
for (int i = 0; i < 5; i++)
{
await Task.Delay(100); // Simulate an I/O operation
totalPercentDone[n - 1] += 10;
progress.Report(totalPercentDone.Min());
}
})).ToArray();
await Task.WhenAll(tasks);
}
This is to extend my comments under question
Basically, progress is usually a forward-only value. With regards to reporting progress, it is likely that you never need to report a progress made in the past. Even you do, in most cases the client / event handler side would still drop such values received.
The problem here / why you need to synchronize reporting is mainly because you are reporting a progress of value type, whose value got copied when Report(T) is called.
You can simply avoid locking by reporting a reference type instance with the latest progress made:
public class DoStuffProgress
{
private volatile int _percentage;
public int Percentage => _percentage;
internal void IncrementBy(int increment)
{
Interlocked.Add(ref _percentage, increment);
}
}
Now your code looks like:
async Task DoStuffAsync(IProgress<DoStuffProgress> progress)
{
DoStuffProgress totalPercentDone = new DoStuffProgress();
Task[] tasks = Enumerable.Range(1, 2).Select(n => Task.Run(async () =>
{
for (int i = 0; i < 5; i++)
{
await Task.Delay(100); // Simulate an I/O operation
totalPercentDone.IncrementBy(10);
// Report reference type object
progress.Report(totalPercentDone);
}
})).ToArray();
await Task.WhenAll(tasks);
}
The client, however, may receive notification with duplicate value:
Progress: 20
Progress: 20
Progress: 40
Progress: 40
Progress: 60
Progress: 60
Progress: 80
Progress: 80
Progress: 90
Progress: 100
But, the values should never be out of order.
Related
I'm trying to show a progress dialog showing the percent done with the method to wait everything + an await Task.Delay(20); And an await for the method I want to execute. Now I notice that with that task.delay the execution takes much longer.
What I want to achieve instead is that the progress.dialog calculates how long the method takes instead of putting a delay on it because this works a bit more slowly.
What are my options in this?
This is my code
private async Task DownloadAllAlert()
{
//alert to download everything
bool result = await DisplayAlert("Download", "Do you want to download everything?", "Yes", "No"); ;
//alert is user chose yes
if (result)
{
// loading dialog in percentage till downloading is done
using (var progress = UserDialogs.Instance.Progress("Loading..."))
{
for (var i = 0; i < 100; i++)
{
progress.PercentComplete = i;
await Api.DownloadAll();
await Task.Delay(20);
}
}
}
}
try using Task in c#.
using (var progress = UserDialogs.Instance.Progress("Loading..."))
{
await LoadData(ref progress);
}
call this function
public async Task LoadData(ref IProgressDialog progress);
{
//await Task.Yield(); //add this line of code if on ios didnt work
Task task1 = new Task(() =>
{
for (int i = 0; i < 50 ; i++) // loop untill 50% then increase sleeping thread time
{
progress.PercentComplete = i;
//sleeping for 1 second
Thread.Sleep(1000);
}
for (int i = 50; i < 100; i++) // loop untill 99% then stop
{
progress.PercentComplete = i;
//sleeping for 2 second
Thread.Sleep(2000);
}
Console.WriteLine("Task 1 complete");
});
Task task2 = new Task(() =>
{
await Api.DownloadAll();
Console.WriteLine("Task 2 complete");
});
//starting the tasks
task1.Start();
task2.Start();
Task.WaitAny(task2);
progress.PercentComplete = 100; // when task2 finish put it 100%
}
This is a follow up question from this question: How do you run a variable number of concurrent parametrizable infinite loop type of threads in C#?
Say I have a value taskLimit (assume 20), of no of simultaneous Tasks "MyTask" which are created in the RunAsync method below:
protected override async Task RunAsync(CancellationToken cancellationToken)
{
var tasks = new List<Task>();
try
{
for (int i = 0; i < taskLimit; i++)
{
tasks.Add(MyTask(cancellationToken, i);
}
await Task.WhenAll(tasks);
}
catch (Exception e)
{
//Exception Handling
}
}
public async Task MyTask(CancellationToken cancellationToken, int a)
{
while (true)
{
cancellationToken.ThrowIfCancellationRequested();
try
{
//long running code, if possible check for cancellation using the token
//Do something useful here. Very Processor and IO heavy. Takes 5-10 minutes to complete.
//SomeHeavyTask can only concurrently run upto a limit of say 5. Implement a token system of sorts
while(freeTokens<1)
{
await Task.Delay(TimeSpan.FromMinutes(1), cancellationToken);
}
freeTokens = freeTokens-1;
SomeHeavyTask(cancellationToken);
freeTokens = freeTokens+1;
//sleep for an independently parameterizable period, then wake up and repeat
await Task.Delay(TimeSpan.FromHours(parametrizableTaskDelay[i]), cancellationToken);
}
catch (Exception e)
{
//Exception Handling
}
}
}
Is it possible to do such a thing? Is there a better more formal approach supported natively in C# to achieve the same thing? Please note the essence of this question is that there are substantially less freeTokens than the taskLimit. And that each MyTask spends only 10% time in SomeHeavyTask(), and most of the time is spent in await Task.Delay().
You should use Microsoft's Reactive Framework (aka Rx) - NuGet System.Reactive and add using System.Reactive.Linq; - then you can do this:
int taskLimit = 500;
int maxConcurrent = 5;
IObservable<Unit> query =
Observable
.Range(0, taskLimit)
.Select(x => Observable.FromAsync(ct => SomeHeavyTask(ct)))
.Merge(maxConcurrent);
await query;
That's a lot easier to work with in my book.
Another option:
var block = new ActionBlock<int>(x => SomeHeavyTask(cancellationToken, x),
new ExecutionDataflowBlockOptions()
{
MaxDegreeOfParallelism = 20,
CancellationToken = cancellationToken
});
for (int i = 0; i < 100; i++)
await block.SendAsync(i, cancellationToken);
block.Complete();
await block.Completion;
You could use a SemaphoreSlim to limit the number of tasks that are working concurrently (you will still have taskLimit Tasks active, but only a limited number of those will be doing the heavy work simultaneously; I assume this is what you want).
This is best demonstrated with a sample console app. If you run this you'll see from the output that a maximum of 5 "heavy tasks" are active simultaneously.
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
namespace Demo
{
class Program
{
static async Task Main()
{
Console.WriteLine("Starting");
// Cancel after 30 seconds for demo purposes.
using var source = new CancellationTokenSource(TimeSpan.FromSeconds(30));
await RunAsync(source.Token);
Console.WriteLine("Stopped.");
Console.ReadLine();
}
public static async Task RunAsync(CancellationToken cancellationToken)
{
int taskLimit = 20;
int concurrencyLimit = 5;
var sem = new SemaphoreSlim(concurrencyLimit);
var tasks = new List<Task>();
try
{
for (int i = 0; i < taskLimit; i++)
{
int p = i; // Prevent modified closure.
tasks.Add(Task.Run(() => MyTask(cancellationToken, p, sem)));
}
await Task.WhenAll(tasks);
}
catch (OperationCanceledException)
{
Console.WriteLine("Task(s) were cancelled.");
}
catch (Exception e)
{
// Exception Handling
}
}
public static async Task MyTask(CancellationToken cancellationToken, int a, SemaphoreSlim sem)
{
while (true)
{
cancellationToken.ThrowIfCancellationRequested();
try
{
await sem.WaitAsync(cancellationToken);
try
{
someHeavyTask(cancellationToken, a);
}
finally
{
sem.Release();
}
}
catch (OperationCanceledException)
{
Console.WriteLine("Task was cancelled.");
return;
}
catch (Exception e)
{
//Exception Handling
}
}
}
static int heavyTaskCount;
static void someHeavyTask(CancellationToken cancel, int a)
{
int n = Interlocked.Increment(ref heavyTaskCount);
Console.WriteLine("Starting heavy task. Number of simultaneous heavy tasks = " + n);
// Simulate work. Make the work for each task take varying time by using 'a' for the sleep.
for (int i = 0; i < 20 && !cancel.IsCancellationRequested; ++i)
{
Thread.Sleep(100 + a*10);
}
n = Interlocked.Decrement(ref heavyTaskCount);
Console.WriteLine("Finishing heavy task. Number of simultaneous heavy tasks = " + n);
}
}
}
The core of this is controlled by the semaphore in the code here:
await sem.WaitAsync(cancellationToken);
try
{
someHeavyTask(cancellationToken, a);
}
finally
{
sem.Release();
}
Like #mjwills said, you can use a C# semaphore to manage concurrent access to resources. (random example)
I do recommend looking at existing solutions first though. For example, Hangfire.
You can store its state inside SF if needed.
This question already has answers here:
How to delay 'hot' tasks so they can processed in a set order
(2 answers)
Closed 4 years ago.
I need to first create new task then do some remaining work and then start the task that works with its result.
Simplified example:
static int value;
static async Task work1()
{
do
{
int i;
for (i = 0; i < 10000000; i++) {} // some calculations
Console.WriteLine("result1: " + value + " i: " + i);
await Task.Delay(2000).ConfigureAwait(false);
} while (condition);
}
static async Task work2()
{
do
{
int i;
for (i = 0; i < 10000000; i++) {} // some calculations
Console.WriteLine("result2: " + value + " i: " + i);
await Task.Delay(2000).ConfigureAwait(false);
} while (condition);
}
static void Main(string[] args)
{
Task task;
int tempvalue = 100;
if (condition1)
{
tempvalue *= 10;
task = new Task(() => work1());
} else
{
tempvalue -= 5;
task = new Task(() => work2());
}
if (tempvalue > 100)
{
value = 5;
} else
{
value = tempvalue;
}
task.Start();
// immediately do remaining work
}
this code does exactly what I need but compiler shows following warning:
Warning CS4014 Because this call is not awaited, execution of the current method continues before the call is completed. Consider applying the 'await' operator to the result of the call.
on line:
Task task = new Task(() => work());
should I rather use it like so? Is there any difference?
Task task = new Task(async () => await work());
This is not a duplicate of How to delay 'hot' tasks so they can processed in a set order because after task.Start(); it should do remaining work immediately.
Func<Task> f = () => work();
// do stuff
f(); // blocks thread until work1() or work2() hits await
// do remaining work
The async keyword means that the task within your task is asynchronous and using await will mean that you want to wait for the method work to finish.
You could also use Task.Wait() in order to wait for the method to finish it's execution.
But using async await is the better way to do it because it's not blocking the main thread.
I've been using Progress<T> and wondered if it can be replaced by Action<T>.
In the code below, using each of them for reporting progress, i.e. ReportWithProgress() or ReportWithAction(), didn't make any noticeable difference to me. How progressBar1 increased, how the strings were written on the output window, they seemed the same.
// WinForm application with progressBar1
private void HeavyIO()
{
Thread.Sleep(20); // assume heavy IO
}
private async Task ReportWithProgress()
{
IProgress<int> p = new Progress<int>(i => progressBar1.Value = i);
for (int i = 0; i <= 100; i++)
{
await Task.Run(() => HeavyIO());
Console.WriteLine("Progress : " + i);
p.Report(i);
}
}
private async Task ReportWithAction()
{
var a = new Action<int>(i => progressBar1.Value = i);
for (int i = 0; i <= 100; i++)
{
await Task.Run(() => HeavyIO());
Console.WriteLine("Action : " + i);
a(i);
}
}
But Progress<T> can't be a reinvention of the wheel. There should be a reason why it was implemented. Googling "c# Progress vs Action" didn't give me much help. How is Progress different from Action?
Calling progressBar1.Value = i from a different thread results in the dreaded "cross-thread operation not valid" exception. The Progress class, on the other hand, dispatches the event to the synchronization context captured in the moment of construction:
// simplified code, check reference source for actual code
void IProgress<T>.Report(T value)
{
// post the processing to the captured sync context
m_synchronizationContext.Post(InvokeHandlers, value);
}
private void InvokeHandlers(object state)
{
// invoke the handler passed through the constructor
m_handler?.Invoke((T)state);
// invoke the ProgressChanged event handler
ProgressChanged?.Invoke(this, (T)state);
}
This ensures that all updates to progress bars, labels and other UI elements are done on a (one and only) GUI thread.
So, it only makes sense to instantiate the Progress class outside of the background thread, inside a method which is called on a UI thread:
void Button_Click(object sender, EventArgs e)
{
// since this is a UI event, instantiating the Progress class
// here will capture the UI thread context
var progress = new Progress<int>(i => progressBar1.Value = i);
// pass this instance to the background task
Task.Run(() => ReportWithProgress(progress));
}
async Task ReportWithProgress(IProgress<int> p)
{
for (int i = 0; i <= 100; i++)
{
await Task.Run(() => HeavyIO());
Console.WriteLine("Progress : " + i);
p.Report(i);
}
}
The difference is that with a Progress<T> you have an event where multiple listeners can listen for progress and Progress<T> does capture the SynchonizationContext when the instance is constructed and thus does not need to be invoked to the GUI-thread if created in the GUI-thread.
You can also add multiple listeners to an Action<T> (thanks to #Servy for pointing that out), but each of them are then executed in the thread which invokes the action.
Think of the following extended example, where the Progress<T> will work, but the Action<T> will throw an exception:
private async Task ReportWithProgress()
{
var p = new Progress<int>(i => progressBar1.Value = i);
p.ProgressChanged += (s, e) => progressBar2.Value = e;
Task.Run(() =>
{
for (int i = 0; i <= 100; i++)
{
await Task.Run(() => HeavyIO());
Console.WriteLine("Progress : " + i);
((IProgress<int>)p).Report(i);
}
});
}
private async Task ReportWithAction()
{
var a = new Action<int>(i => progressBar1.Value = i);
a += i => progressBar2.Value = i;
Task.Run(() =>
{
for (int i = 0; i <= 100; i++)
{
await Task.Run(() => HeavyIO());
Console.WriteLine("Action : " + i);
a(i);
}
});
}
Task<string> RunList(int client)
{
return pages[client];
}
private async void Form1_DoubleClick(object sender, EventArgs e)
{
for (int x = 0; x < listBox1.Items.Count; x++)
{
RunList(x);
}
}
This will fly through the loop of tasks, but how do you know when the results are all in without compromising speed of the loop?
You can await on the result of WhenAll to ensure that all of the tasks have completed at that point in code. (It's important not to use WaitAll here, that would block the UI thread.)
private async void Form1_DoubleClick(object sender, EventArgs e)
{
var tasks = new List<Task<string>>();
for (int x = 0; x < listBox1.Items.Count; x++)
{
tasks.Add(RunList(x));
}
await Task.WhenAll(tasks);
}
The basic idea here is to simply start the tasks before calling await on them. Here is a simpler example with just two Tasks:
await Task.Delay(1000);
await Task.Delay(1000);
This will perform the first task and then the second task.
var task1 = Task.Delay(1000);
var task2 = Task.Delay(1000);
await task1;
await task2;
This will start both tasks and then continue on after both tasks have finished, allowing then to run concurrently.