C# .NET 4 Can Task provides notifications on UI thread? - c#

I am using Task class in my app. This is NOT WPF application! The question is are there any possibilities of calling function from Task body on UI thread, like this:
var task = new Task(() => DoSmth(1));
task.Start();
public void DoSmth(int arg)
{
//smth
CallNotifFuncOnUIThread(() => Notify(1));
//smth ELSE
CallNotifFuncOnUIThread(() => Notify(2));//notify AGAIN
//smth ELSE
}
public void Notify(int arg)
{
progressBar1.Value = arg;
}
Or maybe there are other solutions of this problem? I know about BackgroundWorker class, but what about Tasks?

You can always call other methods inside your DoSth()
Dispatcher.Invoke(...);
Dispatcher.BeginInvoke(...);
You can also user Task.ContinueWith(...) to do sth after the task is finished processing ...

If you have a task you can start it on the gui thread by providing the correct scheduler:
Task.Factory.StartNew(() => DoSomethingOnGUI(), TaskScheduler.FromCurrentSynchronizationContext());

UI is usually STA see: http://msdn.microsoft.com/en-us/library/windows/desktop/ms680112(v=vs.85).aspx
so in order to do something from none UI thread in the UI you need to inject somehow the msg into the thread
see for example htis winform example:
http://weblogs.asp.net/justin_rogers/articles/126345.aspx
watever UI you are using you will need a similar system.

With windows Form and progressBar1 component on ityou can use TPL IProgress interface for Task.
private void Form1_Load(object sender, EventArgs e)
{
Progress<int> progress = new Progress<int>();
var task = Alg(progress);
progress.ProgressChanged += (s, i) => { UpdateProgress(i); };
task.Start();
}
public void Notify(int arg)
{
progressBar1.Value = arg;
}
public static Task Alg(IProgress<int> progress)
{
Task t = new Task
(
() =>
{
for (int i = 0; i < 100; i++)
{
Thread.Sleep(100);
((IProgress<int>)progress).Report(i);
}
}
);
return t;
}

Related

C# - WPF - Updating the UI from another class on another Thread

I have looked around the internet and found a nice solution which I am incorporating into my code below however it doesn't quite do exactly what I want, it works when just calling an update but I want to run a method in another class then let that method call the method that will report back to the UI and just pass some information so this mock up is just changing the button content before the operation is ran.
Using a Dispatcher I can get a UI control to update however I don't just wish to do that I want to perform some functions then have the UI Update.
So there maybe some theory I am not getting, I know the Invoke is a synchronous operation and breaking through the code it does run but the UI doesn't update.
MainWindow
Has a single button with content "CLICK ME"
Code Behind
public partial class MainWindow : Window
{
public static Button windowButton;
public MainWindow()
{
InitializeComponent();
windowButton = btnStart;
}
private void btnStart_Click(object sender, RoutedEventArgs e)
{
// Run a Process in another class on a different Thread
ProcessClass processClass = new ProcessClass();
Task processTask = new Task(() =>
{
processClass.DoSomething();
});
processTask.Start();
}
}
}
ProcessClass
class ProcessClass:MainWindow
{
public static void UpdateUI()
{
App.Current.Dispatcher.Invoke(delegate
{
windowButton.Content = "CHANGED CONTENT";
});
}
public void DoSomething()
{
UpdateUI();
int counter = 0;
for(int i = 1; i < 100; i++)
{
counter += i;
Thread.Sleep(100);
}
MessageBox.Show($"Task Completed, answer is {counter}");
}
}
Assuming that ProcessClass is your own code that you can update, change the signiture of DoDomething() to
public async Task DoSomething(IProgress<string> progress)
{
progress.Report("Begin DoSomething()");
var counter = 0;
for(var i = 1; i < 100; i++)
{
counter += i;
await Task.Delay(100).ConfigureAwait(false);
progress.Report($"DoSomething() - i = {i}");
}
progress.Report($"DoSomething() Completed, answer is {counter}");
}
Now your button click handler can be written
private async void btnStart_Click(object sender, RoutedEventArgs e)
{
// usually you would update some other control such as a TextBlock
// for the feedback, rather than the button content
var progress = new Progress<string>(s => btnStart.Content = s);
ProcessClass processClass = new ProcessClass();
await processClass.DoSomething(progress).ConfigureAwait(false);
}

How can I use the ProgressBar with Async?

Controlling the progress bar with the backgroundworker made my project difficult after a certain place. In this case, I decided to move on async structure, I built the architecture on the async structure. But this time I did not know how to control the progress bar under the async structure.
private async void button3_Click(object sender, EventArgs e)
{
progressBar1.Value = 1;
int value = 1;
await ProgressBarControl(value);
await Convert();
}
public Task ProgressBarControl(int e)
{
return Task.Run(() =>
{
var progress = new Progress<int>(percent =>
{
progressBar1.Value = percent;
});
});
}
But it is not working. I used backgroundworker. I was asking this question. But you guys suggested to me backgroundworker. But backgroundworker, after a while have a error and system is not answered to me?
The question is unlcear. BackgroundWorker is osbsolete since 2012 anyway.
If you want to report progress from any background thread, not just threads created with Task.Run, create a Progress<T> in the UI thread and pass it only as an IProgress<T> to the background thread or task, eg :
private async void button3_Click(object sender, EventArgs e)
{
var progress = new Progress<int>(percent =>
{
progressBar1.Value = percent;
});
progressBar1.Value = 1;
int value = 100;
await DoSomeWork(value,progress);
}
public Task DoSomeWork(int iterations,IProgress<int> progress)
{
for(int i=0;i<iterations;i++)
{
await Task.Run(()=>{
DoSomethingReallySlow(i);
progress.Report(i*100/iterations));
});
}
}
Check Async in 4.5: Enabling Progress and Cancellation in Async APIs for an example of both progress reporting and cancellation

Why code after Await does not run on UI thread?

I have used delegates in the past to update the current UI (for example a textbox) with a value that is being processed in my thread. I was under the assumption that async and await took care of this for you.
When I run the code below -when the button is pressed start a new task that will count up to 100 every 100ms. The count should be displayed on the UI, but I get a targetInvocationException instead:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
textBox1.Text = "Press button";
}
private async void button1_Click(object sender, EventArgs e)
{
await Task1();
}
private Task Task1()
{
return Task.Factory.StartNew(() => CountMethod());
}
private void CountMethod()
{
for (int i = 0; i < 100; i++)
{
Task.Delay(100).Wait();
//Console.WriteLine(i.ToString());
textBox1.Text = i.ToString();
}
}
}
The problem is that you are using a thread-pool thread to access the UI. You are doing this by using the Task.Factory.StartNew method.
Here is how you can fix it:
private async Task Task1()
{
for (int i = 0; i < 100; i++)
{
await Task.Delay(100);
textBox1.Text = i.ToString();
}
}
await will capture the current SynchronizationContext so that after the asynchronous wait is complete, the code that follows executes on the UI thread. This is true because await was called by the UI thread (in the case of my example).
In your current code, await is called on a thread-pool thread. In this case the current SynchronizationContext is null and therefore the code that follows the asynchronous wait will be executed also on a thread-pool thread.

Invoking Listbox in threads

I am trying to have my listbox clear it self at the end of my thread. I am having issues invoking it and was hoping someone would show me how.
public delegate void ClearDelegate(ListBox lb);
public void ItemClear(ListBox lb)
{
if (lb.InvokeRequired)
{
lb.Invoke(new ClearDelegate(ItemClear), new Object[] { lb });
}
listBox1.Items.Clear();
}
Quite trivial example using it's own thread (attention just for showing, better here would maybe a BackgroundWorker!):
private Thread _Thread;
public Form1()
{
InitializeComponent();
_Thread = new Thread(OnThreadStart);
}
private void OnButton1Click(object sender, EventArgs e)
{
var state = _Thread.ThreadState;
switch (state)
{
case ThreadState.Unstarted:
_Thread.Start(listBox1);
break;
case ThreadState.WaitSleepJoin:
case ThreadState.Running:
_Thread.Suspend();
break;
case ThreadState.Suspended:
_Thread.Resume();
break;
}
}
private static void OnThreadStart(object obj)
{
var listBox = (ListBox)obj;
var someItems = Enumerable.Range(1, 10).Select(index => "My Item " + index).ToArray();
foreach (var item in someItems)
{
listBox.Invoke(new Action(() => listBox.Items.Add(item)));
Thread.Sleep(1000);
}
listBox.Invoke(new Action(() => listBox.Items.Clear()));
}
According to the MSDN documentation
it is enought to put listBox1.Items.Clear(); statement into else statement.
Another thing is that you can use asynchronous method BeginInvoke that do not block the thread to wait for method finish.
Listbox can be accessed only by the UI thread and not the worker thread.
var sync = SynchronizatoinContext.Current;
sync.Post(delegate {
// Perform UI related changes here
}, null) //as the worker thread cannot access the UI thread resources

Move code that run on a UI thread and access global states to a thread

I am trying to move as much processing out of the UI thread on my Windows Phone app. I have some code that is being executed when I click on a button. The code is conceptually similar to the code below.
private int Processing(int a, int b, int c) {
this.A = this.moreProcessing(a);
this.B = this.moreProcessing(b);
this.C = this.moreProcessing(c);
int newInt = /* ... */
return newInt;
}
public void Button_Click(object sender, EventArgs args) {
var result = Processing(1, 2, 3);
this.MyTextBox.Content = result;
}
That would be very easy to move the execution on that code on a thread if the Processing method wasn't setting/getting global state variables.
How do I make sure that only one thread at a time is running in the right sequence? Right now it is easy since the processing code runs on the UI thread. The nice thing about the UI thread is that it guarantee me that everything runs in the right order and one at a time. How do I replicate that with threads?
I could refactor the entire code to have almost no global state, but cannot necessarily do that right now. I could also use lock, but I am just wondering if there's a better way. The processing I am doing isn't super heavy. However, I sometime see some lag in the UI and I want to keep the UI thread as free as possible.
Thanks!
There are a few approaches.
If you intend to fire up a new thread for every Button_Click event, then indeed you could have multiple threads that wish to write to the same variables. You can solve that by wrapping the access to those variables in a lock statement.
Alternatively, you could have one thread always running dedicated to the Processing thread. Use a BlockingCollection to communicate between the UI thread and the Processing thread. Whenever a Button_Click happens, place the relevant info on the BlockingCollection, and have the Processing thread pull work items off of that BlockingCollection.
Untested code that should be close to OK:
class ProcessingParams // Or use a Tuple<int, int, int>
{
public int A { get; set; }
public int B { get; set; }
public int C { get; set; }
}
BlockingCollection<int> bc = new BlockingCollection<int>();
private int Processing() {
try
{
while (true)
{
ProcesingParams params = bc.Take();
this.A = this.moreProcessing(params.A);
this.B = this.moreProcessing(params.B);
this.C = this.moreProcessing(params.C);
int newInt = /* ... */
return newInt; // Rather than 'return' the int, place it in this.MyTextBox.Content using thread marshalling
}
}
catch (InvalidOperationException)
{
// IOE means that Take() was called on a completed collection
}
}
public void Button_Click(object sender, EventArgs args) {
//var result = Processing(1, 2, 3);
bc.Add (new ProcessingParams() { A = 1, B = 2, C = 3 };
//this.MyTextBox.Content = result;
}
When your application closes down, remember to call
bc.CompleteAdding(); // Causes the processing thread to end
A very simple solution is to use a BackgroundWorker. It allows you to offload your work to a background thread and notify you when it is complete. (see below for another option)
void Button_Click(object sender, EventArgs args)
{
BackgroundWorker worker = new BackgroundWorker();
worker.DoWork += (s, e) =>
{
e.Result = Processing(1, 2, 3);
};
worker.RunWorkerCompleted += (s1, e1) =>
{
MyTextBox.Content = e1.Result;
MyButton.IsEnabled = true;
};
// Disable the button to stop multiple clicks
MyButton.IsEnabled = false;
worker.RunWorkerAsync();
}
Another option is to get your code ready for the next version of Windows Phone and start using the Task Parallel Library. TPL is available with .Net4, but is not available with Windows Phone. There are some NuGet packages that do support Silverlight and Windows Phone. Add one of these packages to your project and you can change your code to (syntax may not be 100% correct):
private Task<int> ProcessAsync(int a, int b, int c)
{
var taskCompletionSource = new TaskCompletionSource<int>();
var task = Task.Factory.StartNew<int>(() =>
{
// Do your work
return newInt;
}
task.ContinueWith(t => taskCompletionSource.SetResult(t.Result));
return taskCompletionSource.Task;
}
void Button_Click(object sender, EventArgs args)
{
// Disable the button to prevent more clicks
MyButton.IsEnabled = false;
var task = ProcessAsync(1,2,3);
task.ContinueWith(t =>
{
MyTextBox.Content = t.Result;
MyButton.IsEnabled = true;
});
}
Try this:
public void Button_Click(object sender, EventArgs args)
{
Button.Enabled = false;
ThreadPool.QueueUserWorkItem(new WaitCallback(BackgroundProcessing));
}
private void BackgroundProcessing(object state)
{
var result = Processing(1, 2, 3);
// Call back to UI thread with results
Invoke(new Action(() => {
this.MyTextBox.Content = result;
Button.Enabled = true;
}));
}
private int Processing(int a, int b, int c)
{
this.A = this.moreProcessing(a);
this.B = this.moreProcessing(b);
this.C = this.moreProcessing(c);
int newInt = /* ... */
return newInt;
}

Categories