Task Thread Safe Calls - c#

I have been using system.threading for a while now and I trying to wrap my head around tasks. How do you make thread safe calls to a UI control (for example a text box) from another thread using the TPL?
Here is a simple example where I want to update a text box everyone 1 second with the count of my secondary thread.
I have tried a few different methods but I can't seem to get it to work.
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private async void button1_Click(object sender, EventArgs e)
{
await taskAsync();
}
private Task taskAsync()
{
return Task.Factory.StartNew(() => counter());
}
private void counter()
{
for (int i = 0; i < 10000; i++)
{
Task.Delay(1000).Wait();
textBox1.Text = i.ToString();
}
}
}
Is this even possible?
Thank you

Well in your current scenario I Think that a Progress would be most suitable.
I've made some alterations to your code below:
public partial class Form1 : Form
{
Progress<int> counterProgress;
public Form1()
{
InitializeComponent();
counterProgress = new Progress<int>();
counterProgress.ProgressChanged += CounterProgressUpdated;
}
private void CounterProgressUpdated(object sender, int e)
{
textBox1.Text = e.ToString();
}
private async void button1_Click(object sender, EventArgs e)
{
await taskAsync(counterProgress);
}
private Task taskAsync(IProgress<int> progress)
{
return Task.Factory.StartNew(() => counter(progress));
}
private async Task counter(IProgress<int> progress)
{
for (int i = 0; i < 10000; i++)
{
await Task.Delay(1000);
progress.Report(i);
}
}
}
Since the progress captures the current synchronization context on construction then you should be good to go as long as you create it on the UI thread.
Any handler provided to the constructor or event handlers registered with the ProgressChanged event are invoked through a SynchronizationContext instance captured when the instance is constructed. If there is no current SynchronizationContext at the time of construction, the callbacks will be invoked on the ThreadPool.

What about something like this?
private void counter() {
for (int i = 0; i < 10000; i++) {
Thread.Sleep(1000);
// “There's never an advantage in replacing Thread.Sleep(1000); in Task.Delay(1000).Wait();”
// http://stackoverflow.com/a/29357131/4267982
Dispatcher.Invoke(() => {
textBox1.Text = i.ToString();
});
}
}
Or, as an option, another way without separate thread at all (so there's no need in synchronization):
private async Task taskAsync() {
for (int i = 0; i < 10000; i++) {
await Task.Delay(1000);
textBox1.Text = i.ToString();
}
}

How do you make thread safe calls to a UI control (for example a text box) from
another thread using the TPL?
You generall do NOT.
If you need to update the UI, then you do that using Invoke. But from TPL directly - you do not want to update the UI too often because this always is a heavy redraw and unless you make a first person shooter frame rate does not matter THAT Much. 10 updates per second are PLENTY.
In this case you may want the tasks to update a central counter (using Interlocked classes) and once that goes above a certain threshhold in changes (or using a timer running in parallel) push that into the UI.

Related

C# - How to send update value back to form class from a class where the work is done in a different thread

I'm working on a Splash/Loading screen for an app and I'd like to have a progress bar as well as a label on it which will notify the user of current process, e.g. connecting to the database, loading user settings, retrieving x data, retrieving y data.
I only did a tiny bit of BackGroundWorker stuff in VB before and nothing in C# so a bit confused where to start as it looks quite different in C#.
I'd like to keep the form class simple as to only calling specific methods from different classes on a different thread.
I mainly need to know how to update the GUI from the class that does the work as I think I could work out the threading itself from the below code:
using System;
using System.Threading;
public class ThreadWork
{
public static void DoWork()
{
for(int i = 0; i<3;i++) {
Console.WriteLine("Working thread...");
Thread.Sleep(100);
}
}
}
class ThreadTest
{
public static void Main()
{
Thread thread1 = new Thread(ThreadWork.DoWork);
thread1.Start();
for (int i = 0; i<3; i++) {
Console.WriteLine("In main.");
Thread.Sleep(100);
}
}
}
You could use the Task.Run method to offload work to a ThreadPool thread, and a Progress<string> object to report progress from the background thread to the UI. You can also use async/await in order to write your code in a straightforward way. Here is an example:
private async void Form_Load(object sender, EventArgs e)
{
IProgress<string> progress = new Progress<string>(message =>
{
Label1.Text = message;
});
var result = await Task.Run(() =>
{
progress.Report("Connecting to database...");
ConnectToDatabase();
progress.Report("Loading user settings...");
LoadUserSettings();
progress.Report("Retrieving x data...");
return RetrieveXData();
});
// At this point the background processing has completed
Label1.Text = $"Done! ({result})";
}
If you have heterogeneous data to report at different intervals, you can use multiple Progress<T> objects (for example a Progress<string>, a Progress<int> etc). You can also use a complex type for reporting, for example Progress<(string, int)>.
Although Mitch's answer will work. You can also use TaskSchedulers to handle background work and then respond back to UI:
public static void Main()
{
TaskScheduler uiScheduler = TaskScheduler.FromCurrentSynchronizationContext();
Task.Factory.StartNew(() => DoBackgroundWork(uiScheduler));
}
private static void DoBackgroundWork(TaskScheduler uiScheduler)
{
// Do background stuff here...
Task.Factory.StartNew(() =>
{
// Respond back to UI here
Console.WriteLine("Doing work");
// Or if you have to change GUI, let's say a label value;
label1.Text = "Text changed by background Task";
}, CancellationToken.None, TaskCreationOptions.None, uiScheduler);
// Some other background work
}

Task.WaitAll freezes app C#

I wanted to try out Threading.Task (C#) to run some work in parallel. In this simple example I have a form with progress bar and button. On click the RunParallel function is called. Without Task.WaitAll() it seems to run through fine. However, with the WaitAll statement the form shows and nothing happens. I dont understand what I am doing wrong in the setup below.
Thanks in advance.
public partial class MainWindow : Form
{
public delegate void BarDelegate();
public MainWindow()
{
InitializeComponent();
}
private void button_Click(object sender, EventArgs e)
{
RunParallel();
}
private void RunParallel() {
int numOfTasks = 8;
progressBar1.Maximum = numOfTasks;
progressBar1.Minimum = 0;
try
{
List<Task> allTasks = new List<Task>();
for (int i = 0; i < numOfTasks; i++)
{
allTasks.Add(Task.Factory.StartNew(() => { doWork(i); }));
}
Task.WaitAll(allTasks.ToArray());
}
catch { }
}
private void doWork(object o1)
{
// do work...
// then
this.Invoke(new BarDelegate( UpdateBar ));
}
private void UpdateBar()
{
if (progressBar1.Value < progressBar1.Maximum) progressBar1.Value++;
}
}
You are waiting on the UI thread. This freezes the UI. Don't do that.
In addition, you are deadlocking because Invoke waits for the UI thread to unblock.
My advice: use async/await if at all possible.
And please do not swallow exceptions. You're creating future work for yourself that way.
That's what WaitAll does, it blocks until all tasks have finished. The tasks can't finish because Invoke will run its action on the UI thread, which is already blocked by WaitAll
To make your code really run asynchronously, try something like this:
private void RunParallel() {
int numOfTasks = 8;
progressBar1.Maximum = numOfTasks;
progressBar1.Minimum = 0;
try
{
var context=TaskScheduler.FromCurrentSynchronizationContext()
for (int i = 0; i < numOfTasks; i++)
{
Task.Factory.StartNew(()=>DoWork(i))
.ContinueWith(()=>UpdateBar(),context);
}
}
catch (Exception exc)
{
MessageBox.Show(exc.ToString(),"AAAAAARGH");
}
}
private void DoWork(object o1)
{
// do work...
}
private void UpdateBar()
{
if (progressBar1.Value < progressBar1.Maximum) progressBar1.Value++;
}
In this case, UpdateBar is called on the UI context each time a task finishes without causing any blocking.
Note, this is not production code, just a way to show how you can run a method asynchronously and update the UI without blocking. You do need to read about Tasks to understand what they do and how they work.
Using async/await in .NET 4.5+ you can write this in a much simpler way. The following will execute DoWork in the background without blocking, but still update the UI each time a Task finishes.
private async void button1_Click(object sender, EventArgs e)
{
int numOfTasks = 8;
progressBar1.Maximum = numOfTasks;
progressBar1.Minimum = 0;
try
{
for (int i = 0; i < numOfTasks; i++)
{
await Task.Run(() => DoWork(i));
UpdateBar();
}
}
catch (Exception exc)
{
MessageBox.Show(exc.ToString(), "AAAAAARGH");
}
}
await tells the compiler to generate code to execute anything below it ont the original (UI) threadwhen the task to its right finishes:
In doWork you call this.Invoke(...) which waits for UI thread to process messages. Unfortunatelly you UI thread is not processing messages because it is waiting for all doWork(...) to finish.
Easiest fix is to change this.Invoke to this.BeginInvoke (it will send messages but no wait for them to be processed) in doWork.
Although, I have to admin it is still not by-the-book as UI should not wait for anything.
Simple pattern (pre async/await era):
Task.Factory.StartNew(() => {
... work ...
})
.ContinueWith((t) => {
... updating UI (if needed) ...
}, TaskScheduler.FromCurrentSynchronizationContext());
RunParallel blocks until all the tasks are completed. Use a different mechanism to notify the UI.

How to display a loop's progress without pausing the loop?

I have a for loop (running in its own thread) in which I'm calculating the loop's progress and I want to display the progress value every time it changes, but I want to run the message display command outside the loop, so it doesn't pause the loop.
I have read How do I display progress during a busy loop?, but I don't want to use a background worker because I already have one that uses the instance of the class that starts the loop (i.e. I do not want to nest background workers). I am assuming that the alternative would be raising and listening to events, but I am not sure how to implement that in this case.
So, how can I solve this problem without the use of a background worker?
If it's Winforms, you can just do a MyForm.BeginInvoke() with an anonymous delegate that updates the display of the progress. BeginInvoke is asynchronous so it won't block the current thread.
You need to notify something like a LoopWatcher out of the loop thread.
public class ProgressEventArgs : EventArgs
{
public int Number { get; private set; }
public ProgressEventArgs(int num)
{
this.Number = num;
}
}
public class Worker
{
public event EventHandler<ProgressEventArgs> ProgressChanged = delegate { };
public void DoSomething()
{
for (int i = 0; i < 10; i++)
{
ProgressChanged(this, new ProgressEventArgs(i));
Thread.Sleep(1000); //just an example here
}
}
}
You can see in Worker.DoSomething, there is a loop which costs time. I add an event in Worker, so outside the class, the subscriber can know the progress is changed.
var worker = new Worker();
worker.ProgressChanged += (s, e) => Console.WriteLine(e.Number);
Thread t = new Thread(worker.DoSomething);
t.Start();
Since the provided answers didn't meet my requirements, I did some research on events and solved the issue the way I initially wanted.
I declared an event in the class that is starting the loop:
public delegate void ProgressChangedEvHandler(int progress);
public event ProgressChangedEvHandler ProgressChanged;
private void OnProgressChanged(int progress)
{
var handler = ProgressChanged;
if (handler != null) handler(progress);
}
Then I invoked the event from within the loop:
for (var index = 0; index < arrayListCount; index++)
{
var progress = (int) (100*(double) index/(double) arrayListCount);
OnProgressChanged(progress);
}
and then I just created the listener (LoopClassInstance.ProgressChanged += LoopClassInstance_ProgressChanged;) in a different class (on a different thread):
private void LoopClassInstance_ProgressChanged(int progress)
{
toolStripProgressBar1.Value = progress;
}

My Custom progressBar Form Hangs when i show it

I made a form that plays a progressbar role here's the code i made
public partial class PXProgressBar : Form
{
public delegate bool CancelEvent();
public event CancelEvent cancel_e;
public Boolean ProcessCancelled
{
get;
set;
}
public PXProgressBar(bool EnableCancel)
{
InitializeComponent();
ProcessCancelled = false;
progressBar1.Minimum = 0;
if (!EnableCancel)
Cancelbtn.Visible = false;
}
public void increament(int step)
{
if (progressBar1.Value < progressBar1.Maximum-1)
{
progressBar1.Value++;
progressBar1.Caption = progressBar1.Value.ToString() + " of " + progressBar1.Maximum;
progressBar1.Refresh();
}
else
{
progressBar1.Value++;
progressBar1.Caption = progressBar1.Value.ToString() + " of " + progressBar1.Maximum;
if (this.TopMost)
this.TopMost = false;
this.Update();
this.Hide();
this.WindowState = FormWindowState.Minimized;
// this.Dispose();
}
}
public void SetMaximum(int MaximumValue)
{
if (MaximumValue <= 0)
{
progressBar1.Maximum = 0;
return;
}
if (progressBar1.Minimum != 0 && MaximumValue < progressBar1.Minimum)
{
progressBar1.Maximum = progressBar1.Minimum;
return;
}
progressBar1.Maximum = MaximumValue;
}
public void SetMinimum(int MinimumValue)
{
progressBar1.Value = 0;
if (MinimumValue <= 0)
{
progressBar1.Minimum = 0;
return;
}
if (progressBar1.Maximum != 100 && MinimumValue > progressBar1.Maximum)
{
progressBar1.Minimum = progressBar1.Maximum;
return;
}
progressBar1.Minimum= MinimumValue;
}
public void SetTitle(string ProcessTitle)
{
this.ProgressTitlelb.Text =ProcessTitle;// ProcessTitle;
//this.ProgressTitlelb.Left = (this.panel1.Width - this.ProgressTitlelb.Width) / 2;
//this.ProgressTitlelb.Top = (this.panel1.Height - this.ProgressTitlelb.Height) / 2;
this.Update();
}
private void Cancelbtn_Click(object sender, EventArgs e)
{
ProcessCancelled = true;
bool disposeRequired =cancel_e();
if(disposeRequired)
this.Dispose();
}
private void PXProgressBar_Shown(object sender, EventArgs e)
{
this.Update();
}
}
and i call the form through this code
if (ProgressBar == null)
ProgressBar = new PXProgressBar(true);
ProgressBar.SetTitle("Saving ...");
ProgressBar.SetMinimum(0);
ProgressBar.SetMaximum(100);
ProgressBar.TopMost = true;
ProgressBar.Show();
Application.DoEvents();
regarding that the past few lines are in a unction that is called throught a thread
but when i run it the form hangs so i cant set a Cancel Button in the form to let the user cancel the operation
Your code looks like it should be fine so I can only assume that you are doing a long running operation on the UI thread which would cause the UI to look like its hung. You need to perform long running operations on a background thread so that the UI thread remains responsive enough to respond to button clicks etc. There are many many articles about this if you consult your friend Google.
More info on that here http://www.idevforfun.com/index.php/2010/01/10/windows-ui-threading/
I agree with Skizz on the DoEvents call ... there's only a very few rare cases where that call is needed and mostly its in the framework itself that it gets used.
You need to make sure the GUI elements are created on the main form thread and not from a separate thread. So, you need to get you thread that is doing the work to get the main form thread to display and update the progress bar. This is going to take a bit of refactoring.
So, in your worker thread:
void DoWork () // of whatever it's called
{
main_form.CreateProgressBar ();
while (doing stuff)
{
main_form.IncrementProgressBar ();
do stuff
}
main_form.DestroyProgressBar ();
}
And in the main form:
delegate void Callback ();
void CreateProgressBar ()
{
if (InvokeRequired)
{
Invoke (new Callback (CreateProgressBar));
}
else
{
progress_bar = CreateProgressBar ();
}
}
void IncrementProgressBar ()
{
if (InvokeRequired)
{
Invoke (new Callback (IncrementProgressBar ));
}
else
{
progress_bar.IncrementProgressBar ();
}
}
void DestroyProgressBar ()
{
if (InvokeRequired)
{
Invoke (new Callback (DestroyProgressBar));
}
else
{
progress_bar.Close ();
progress_bar = null;
}
}
The InvokeRequired determines if the calling thread is the same as the GUI thread. If the calling thread is not the GUI thread, the Invoke is used to changed thread context. This is the synchronous version and won't complete until the invoked method is finished. There is an asynchronous version called BeginInvoke but this isn't really needed for what your doing.
The problem might be the DoEvents method call. From this MSDN page:
Calling this method causes the current thread to be suspended while
all waiting window messages are processed. If a message causes an
event to be triggered, then other areas of your application code may
execute. This can cause your application to exhibit unexpected
behaviors that are difficult to debug. If you perform operations or
computations that take a long time, it is often preferable to perform
those operations on a new thread. For more information about
asynchronous programming, see Asynchronous Programming Overview.
I don't think the DoEvents call is necessary. If you need to halt the code after the Show for the operation to complete, then use a System.Threading.EventWaitHandle instead.
There's some link maybe helpful for you about progressbar:
How do I implement a progress bar in C#?
Hope this help.
Just create other thread for progressbar and use it in background

Task Parallel with .net framework 4

I am studying parallelism and would like to know which way do you recommend for me to access other thead elements, for example, imagima I'll fill a combobox with some names, query the database I would do in parallel but I could not do a combobox.add (result) from within the task, which way do you recommend me?
a simple example to understand my question:
private void button1_Click (object sender, EventArgs e)
{
Task task = new Task (new Action (Count));
task.Start ();
}
void Count ()
{
for (int i = 0; i <99; i + +)
{
Thread.Sleep (1);
progressBar1.Value = i;
}
}
time to pass the value for the progressbar result in error
If you want to schedule a task that access UI controls, you need to pass the current synchronization context to the scheduler. If you do that the scheduler will make sure your task is executed on the correct thread. E.g.
var uiScheduler = TaskScheduler.FromCurrentSynchronizationContext();
Task.Factory.StartNew(() => {
// code that access UI controls
}, uiScheduler);
For more info see http://msdn.microsoft.com/en-us/library/dd997402.aspx
You cannot access controls on another thread directly. You must invoke them first. Read this article: http://msdn.microsoft.com/en-us/library/ms171728.aspx
This is about what is would look like if you took the article and translated it for your own use: (NOT TESTED)
delegate void SetProgressBarCallback();
private void SetProgressBar()
{
// InvokeRequired required compares the thread ID of the
// calling thread to the thread ID of the creating thread.
// If these threads are different, it returns true.
if (this.progressBar1.InvokeRequired)
{
SetProgressBarCallback d = new SettProgressBarCallback(SetProgressBar);
this.Invoke(d);
}
else
{
for(int i=0; i<99; i++)
{
Thread.Sleep(1);
progressBar1.Value = i;
}
}
}
Just a quick note... the UI in WinForms can only be updated from the UI thread. Perhaps you should consider using Control.Invoke to update your progressBar1.
Ryan's answer was correct but he put the sleep inside the invoke, that caused the program to hang. Here is a example that uses the same thing he did but it does not put the sleep in the invoke.
private void button1_Click (object sender, EventArgs e)
{
Task task = new Task (new Action (Count));
task.Start ();
}
void Count ()
{
for (int i = 0; i <99; i + +)
{
Thread.Sleep (1);
if(progressBar1.InvokeRequired)
{
int j = i; //This is required to capture the variable, if you do not do this
// the delegate may not have the correct value when you run it;
progressBar1.Invoke(new Action(() => progressBar1.Value = j));
}
else
{
progressBar1.Value = i;
}
}
}
You must do the int j = i to do variable capture, otherwise it could bring up the wrong value for i inside the loop.

Categories

Resources