How is Progress<T> different from Action<T> ? (C#) - c#

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);
}
});
}

Related

How to synchronize a shared IProgress<int>

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.

initialize task and start it later [duplicate]

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.

C# synchronize wpf

I have a project at school to make a WPF project which makes encryption and decryption of an input text. I want the application to be responsive but it always freeze.
I want to use TPL and I use TaskScheduler.FromCurrentSynchronizationContext() but it is not working. I don't want to use Dispatcher or something else what is specific only to WPF.
tokenSource = new CancellationTokenSource();
int lineCount = textBoxInput.LineCount;
string encryptTextInput = "";
List<string> listText = new List<string>();
List<Task> listTask = new List<Task>();
var ui = TaskScheduler.FromCurrentSynchronizationContext();
for (int cnt = 0; cnt < lineCount; cnt++)
{
encryptTextInput = textBoxInput.GetLineText(cnt);
listText.Add(encryptTextInput);
}
for (int cnt = 0; cnt < lineCount; cnt++)
{
int line = cnt;
var myTask = Task.Factory.StartNew(result =>
{
return EncryptDecrypt.Encrypt(listText[line]);
}, tokenSource.Token);
listTask.Add(myTask);
var display = myTask.ContinueWith(resultTask =>
textBoxOutput.Text += myTask.Result.ToString(), CancellationToken.None, TaskContinuationOptions.OnlyOnRanToCompletion, ui);
var displayCancel = myTask.ContinueWith(resultTask =>
textBoxOutput.Text += myTask.Result.ToString(), CancellationToken.None, TaskContinuationOptions.OnlyOnCanceled, ui);
}
Refactored method which relates to Encryption. Please, see the comments related to the code below:
private async void buttonEncrypt_Click(object sender, RoutedEventArgs e)
{
string elapsedTime = string.Empty;
Stopwatch stopWatch = new Stopwatch();
stopWatch.Start();
tokenSource = new CancellationTokenSource();
int lineCount = textBoxInput.LineCount;
var outputResult = String.Empty;
for (int cnt = 0; cnt < lineCount; cnt++)
{
var lineToProcess = textBoxInput.GetLineText(cnt);
//Code inside task will work in thread from thread pool, so the UI thread shouldn't be blocked
string result = await Task.Run(() =>
EncryptDecrypt.Encrypt(lineToProcess), tokenSource.Token);
outputResult += result;
}
//UI thread: when completed update the UI with encrypted text.
textBoxOutput.Text = outputResult;
stopWatch.Stop();
TimeSpan ts = stopWatch.Elapsed;
elapsedTime = String.Format("{0:00}:{1:00}:{2:00}:{3:00}", ts.Hours, ts.Minutes, ts.Seconds, ts.Milliseconds / 10);
time.Content = elapsedTime;
}
A couple of comments related to the code above.
The code working in the following way:
Read lines from textbox line by line.
Processes each line one by one
(in thread pool context) in order the lines are in input
When processing of all lines completed, add the result of encryption to the
output textbox
The problem in previous code was that the UI thread was accessing too frequently and this lead to UI freezing during processing.
Now the processing is going in background thread and rendered on UI only when all processing completes.
Also, I recommend you to add some kind of indicator to inform the user that the input is processing: progress bar or something other.

how to make form delay a display

edit: Application.DoEvents(); this did it. found here: Force GUI update from UI Thread
c#, winforms. i want to increase a number by steps of 1 and have those increments shown inside a listview, so that the user sees the number counting up (for example from 10 to 15).
i have another button that increments just by 1 when clicked and uses the same display(). it works fine and updates the display as expected.
i tried these codes (shortened to save space here):
(1)
for (int i = 0; i < 5; i++)
{
var t = Task.Run (async () =>
{
myInt++;
await Task.Delay(300);
display(); //forces screen refresh
});
}
(2)
for (int i = 0; i < 5; i++)
{
var t = Task.Run (() =>
{
myInt++;
Task.Delay(300).Wait;
display();
});
//t.Wait(); //causes "An unhandled exception of type 'System.AggregateException' occurred in mscorlib.dll"
}
(3)
for (int i = 0; i < 5; i++)
{
myInt++;
display();
System.Threading.Thread.Sleep(300);
}
(4)
Stopwatch stopwatch = new Stopwatch();
for (int i = 0; i < 5; i++)
{
stopwatch.Restart();
while (true)
{
if (stopwatch.ElapsedMilliseconds >= 300)
{
break;
}
}
stopwatch.Stop();
myInt++;
display();
}
all use this:
private void display()
{
myListView.Items.Clear();
myListView.Items.Add(new ListViewItem(new[] { myInt }));
}
(1) and (2) increment the number by 5 but the display is not updated at all. it shows when the display is updated by some other function.
(3) and (4) increment the number by 5, but the display is only updated after about 1500ms, the display skipping the single steps and displaying just the final result (eg 15).
any suggestions to make this work? can i force a refresh in the display() function somehow?
why does t.Wait(); cause an exception? (i found the task code somewhere on the net)
edit:
(5)
private void team_comm_btn_all_Click(object sender, EventArgs e)
{
for (int i = 0; i < 5; i++)
{
await Run(i); //error 74 see below
}
}
private async Task Run(int i)
{
myInt++;
display();
await Task.Delay(300);
}
await Run(i); gives the following:
Error 74 The 'await' operator can only be used within an async method. Consider marking this method with the 'async' modifier and changing its return type to 'Task'.
just doing "Run(i)" instead gives a warning that "await" is missing... in this case it compiles and increments by 5 without any delay.
(6)
private void team_comm_btn_all_Click(object sender, EventArgs e)
{
for (int i = 0; i < 5; i++)
{
var task = Task.Run(async () =>
{
await Run(i);
});
}
}
private async Task Run(int i)
{
myInt++;
display();
await Task.Delay(300);
}
increments by 5 but does not update display at all.
Normally you would use System.Windows.Forms.Timer for that. But async/await makes such things trivial as soon as you understand it (and read carefully the compiler warning and error messages). Shortly, (5) is the way to go with a small modification resolving the compiler error.
But let start from the beginning. Assuming you first write a normal synchronous version like this (basically your (3))
private void team_comm_btn_all_Click(object sender, EventArgs e)
{
for (int i = 0; i < 5; i++)
{
if (i != 0) System.Threading.Thread.Sleep(300);
myInt++;
display();
}
}
just to see that the UI is not updating. So you decide to turn it into asynchronous, which with the help of async/await is simple replacing Thread.Sleep with Task.Delay like this
private void team_comm_btn_all_Click(object sender, EventArgs e)
{
for (int i = 0; i < 5; i++)
{
if (i != 0) await Task.Delay(300);
myInt++;
display();
}
}
But now it fails to compile with the following error
Error 74 The 'await' operator can only be used within an async method. Consider marking this method with the 'async' modifier and changing its return type to 'Task'.
The message is clear enough. You must mark your method with the async keyword. What about the other recommendation of changing the return type to Task, normally you should take it into consideration, but since you are inside a event handler, i.e. a method which signature cannot be changed, you can ignore that part - that's the exact case why async void is allowed.
To conclude, your final code will look like this
private async void team_comm_btn_all_Click(object sender, EventArgs e)
{
for (int i = 0; i < 5; i++)
{
if (i != 0) await Task.Delay(300);
myInt++;
display();
}
}

Updating multiple progress bars from multiple tasks in task list using IProgress<T>

I have multiple tasks that will be running in parallel. They are gathered in a list of tasks. Each task has a progress bar associated with it. I've been trying to implement the IProgress API in order to update the progress bars while the task runs. However, the progress bar only updates when the task is completed.
I have a set of test code that simulates my problem, and no matter how I modify it, the task bars only update when the task is completed.
As each task is created, I pass Progress object that will update the right progress bar. This Progress object is passed to the inner task to be updated with each loop. The Progress.Report() method will check for an InvokeRequired, although I don't think this should be necessary if you're using the IProgress API with asynchronous methods.
I've included the code. The form simply has a button (button1) that starts all the processes, and there are 11 progress bars that should update with each task.
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private async void button1_Click(object sender, EventArgs e)
{
List<ProgressBar> listOfProgressBars = new List<ProgressBar>
{
progressBar1,
progressBar2,
progressBar3,
progressBar4,
progressBar5,
progressBar6,
progressBar7,
progressBar8,
progressBar9,
progressBar10,
progressBar11
};
List<Task<bool>> taskList = new List<Task<bool>>();
foreach (ProgressBar pBar in listOfProgressBars)
{
pBar.Value = 0;
pBar.Minimum = 0;
pBar.Maximum = 100;
}
int i = 11;
for (int j = 1; j <= i; j++)
{
ProgressBar thisProgressBar = listOfProgressBars[j - 1];
var progress = new Progress<int>((int value) =>
{
UpdateProgressBar(thisProgressBar, value);
});
taskList.Add(InnerProcess(j, progress));
}
await Task.WhenAll(taskList);
}
public void UpdateProgressBar(ProgressBar pBar, int value)
{
if (this.InvokeRequired)
{
this.EndInvoke(this.BeginInvoke(new MethodInvoker(delegate() { UpdateProgressBar(pBar, value); })));
}
else
{
pBar.Value = value;
}
}
public Task<bool> InnerProcess(int waitTime, IProgress<int> progress)
{
return Task<bool>.Run(() =>
{
var job = new LongJob();
job.Delay(waitTime, progress);
return true;
});
}
class LongJob
{
public void Delay(int i, IProgress<int> progress)
{
for (int j = 1; j <= i; j++)
{
Thread.Sleep(500);
if (progress != null)
{
progress.Report(j/i*100);
}
}
}
}
}
progress.Report(j/i*100);
j/i is integer division, since j is less that i until completion, j/i is 0.
cast j to a float and all should be good
You have a bug in your progress calculation due to integer division. Change it to this:
progress.Report((int)(((double)j) / i * 100));

Categories