The UI can be passed information from an asynchronous task using IProgress or BackgroundWorker.ReportProgress:
class Approach1 : UserControl
{
enum Stage { INIT, STATUS, DATA, TIME, ... }
struct ProgressObject
{
int StatusCode;
int SecondsRemaining;
IList<int> Data;
Stage CurrentStage;
}
TextBox Status;
async Task DoWork1(IProgress<ProgressObject> progress)
{
await Task.Run( () =>
{
progress.Report(new ProgressObject(0, 0, null, Stage.INIT));
int code = DoSomething();
progress.Report(new ProgressObject(code, 0, null, Stage.STATUS));
IList<int> Data = ...;
progress.Report(new ProgressObject(0, 0, Data, Stage.DATA));
int Seconds = ...;
progress.Report(new ProgressObject(0, time, null, Stage.TIME));
});
}
void ReportProgress(ProgressObject progress)
{
switch (progress.CurrentStage)
{
case Stage.CODE:
Status.Text = DecodeStatus(progress.StatusCode);
break;
// And so forth...
}
}
async void Caller1(object sender, EventArgs e)
{
var progress = new Progress<ProgressObject>(ReportProgress);
await DoWork2(progress);
}
}
However, this can also be done by passing a delegate to a UI object's BeginInvoke method (Invoke if we want to block):
class Approach2 : UserControl
{
Textbox Status;
int StatusCode
{
set
{
BeginInvoke(new Action( () => Status.Text = DecodeStatus(value));
}
}
// Imagine several other properties/methods like the above:
int SecondsRemaining;
void UpdateData(IList<int> data);
async Task DoWork2()
{
await Task.Run( () =>
{
StatusCode = DoSomething();
UpdateData(...);
SecondsRemaining = ...;
});
}
async void Caller2(object sender, EventArgs e)
{
await DoWork1();
}
}
Should the dedicated progress reporting mechanisms be preferred over Invoke? If, so why? Are there any likely 'gotchas' arising from either approach?
IMHO, the Invoke way is simpler / requires less code compared to, say, a ReportProgress accepting a progress struct with several fields, especially if progress is reported at multiple stages of the task and the reporting method thus needs to branch to the appropriate reporting for a given stage.
You should have gotten a cue from your struggles to make Approach2 actually compile. Took a while, didn't it? I saw you repeatedly editing the snippet. Another cue you got was that the only way to get there was to derive your class from UserControl.
Which is the problem with Begin/Invoke(), it can only work when you have access to a Control object. Code inside a library often (and should) have no idea what the UI looks like. It might not even be implemented in Winforms, could be used in a WPF or Universal app for example.
Progress<> works with those GUI class libraries as well, it uses a more universal way to properly synchronize. Provided by the SynchronizationContext.Current property, it relies on the GUI class library to install a provider. The one that Winforms installs, WindowsFormsSynchronizationContext, automatically calls BeginInvoke() in its Post() method and Invoke() in its Send() method. Also the mechanism that makes async/await code independent from the UI implementation.
There is one disadvantage to Progress<>, it can completely fail to get the job done in a very hard to diagnose way. The object must be created by code that runs on the UI thread of an app. If it is not then SynchronizationContext.Current doesn't have a value and the ProgressChanged event is going to fire on an arbitrary threadpool thread. Kaboom if you try to update the UI with an event handler, you won't know why because the exception occurs in code that's far removed from the bug.
But sure, if you hardcode your class to derive from System.Windows.Forms.UserControl then you have little use for Progress<>. Other than the feel-good feeling that you'll have less work to do when you ever port it to another GUI class library.
Related
This question already has answers here:
Where is the WPF Timer control?
(4 answers)
Closed 4 years ago.
How can I create an async structure that will be consist of stack of delegates and popping them and invoke each of them every N ms?
The problem is now I have lot delegates that invoke changes on ui and it causes ui freezing so how to make this delegates invoking every N ms if stack is not empty.
Now I have this
class CallbackRestriction
{
private Stack<KeyValuePair<Action<ImageWrapper>, ImageWrapper>> _callbackList =
new Stack<KeyValuePair<Action<ImageWrapper>, ImageWrapper>>();
public void AddCallback(Action<ImageWrapper> action, ImageWrapper payload)
{
_callbackList.Push(new KeyValuePair<Action<ImageWrapper>, ImageWrapper>(action, payload));
}
private async Task CallbackEmitLoop()
{
while (true)
{
await Task.Delay(TimeSpan.FromMilliseconds(20));
try
{
var callback = _callbackList.Pop();
callback.Key.Invoke(callback.Value);
}
catch (Exception e)
{
await Task.Delay(200);
}
}
}
}
But how can I make CallbackEmitLoop start in the background? Or any other solution for this?
Update 1
I do not need the dispather timer because is tighten with wpf and maybe for "timer" things I should use synchronization context. And I don't have problems with calling to my collection from others context because collection can be made concurrency ready. I need something like a valve that would restrict invoking delegates once they have been added. So how I described problem above I can get a lot of "updates"(delegates) at one time and if I just apply them(call delegates) the ui thread would be busy significant time that will cause freezing and because of this I somehow should keep times before apply next "update".
Here's one way. The code below uses your CallbackRestriction class and my dummy implementation of ImageWrapper. I've made the CallbackEmitLoop method public so that my window can start it with Task.Run.
Because I maintain the delegate emitter instance in my window, it will run as long as the window is alive. A real app would likely run it from some other service class.
The callback needs to use Dispatcher to invoke code on the UI thread if it needs to work with WPF UI elements because the Task runs on a thread pool thread, and any delegate invocations will run on that thread too.
Regarding the comment that this may be a duplication question, the OP is asking how to have a running Task invoke delegates that interact with the UI, and while DispatcherTimer is certainly a reasonable approach, it doesn't address the OP's question, nor does it offer an explanation as to why DispatcherTimer would be a more appropriate implementation.
// My dummy ImageWrapper
public class ImageWrapper
{
public string Val { get; set; }
}
public partial class MainWindow
{
private CallbackRestriction _restriction = new CallbackRestriction();
public MainWindow()
{
InitializeComponent();
_restriction.AddCallback(MyCallback, new ImageWrapper() {Val = "Hello"});
Task.Run(_restriction.CallbackEmitLoop);
}
private void MyCallback(ImageWrapper wrapper)
{
// since the callback will be running on the
// thread associated with the task, if you
// want to interact with the UI in the callback
// you need to use Dispatcher
Dispatcher.BeginInvoke(new Action(() =>
{
Debug.WriteLine(wrapper.Val);
}));
}
}
For example I use the following code:
private void startCalculcationButton_Click(object sender, EventArgs e) {
int number;
if (int.TryParse(this.numberTextBox.Text, out number)) {
this.calculationResultLabel.Text = "(computing)";
Task.Factory.StartNew(() => {
int result = LongCalculation(number);
this.calculationResultLabel.BeginInvoke(new ThreadStart(() => {
this.calculationResultLabel.Text = result.ToString();
}));
});
}
}
There I call this.calculationResultLabel.BeginInvoke(...) to switch to the UI-Thread for setting the Result to the Label.
I could also use this.Invoke(...) (this is the Form). Does it make a difference internally?
Invoke on Control vs. Invoke on Form
I could also use this.Invoke(...) (this is the Form). Does it make a difference internally?
It sounds like you are asking two different things. Your title says Invoke but your code is using BeginInvoke but you seem to be also asking about whether to invoke on a form or control.
It makes no difference whether you xxxInvoke to the form or a specific control. Both are UI elements and both can marshal calls for you.
If you are asking about the difference between Invoke and BeginInvoke, two methods that can marshal a call to the UI thread via the message pump, there is a difference between them. Invoke can lead to thread deadlock whilst the latter won't.
Anyway, since you are using Task and that you update the UI immediately after the long running task, you are arguably better doing:
private void startCalculcationButton_Click(object sender, EventArgs e) {
int number;
if (int.TryParse(this.numberTextBox.Text, out number))
{
this.calculationResultLabel.Text = "(computing)"; // UI thread
var result = await Task.Run(() =>
LongCalculation(number)); // threadpool thread
calculationResultLabel.Text = result.ToString(); // UI thread
}
}
No fiddly manual marshalling required via BeginInvoke or dangerous-to-use Invoke.
I have a static method, which can be called from anywhere. During execution it will encounter Invoke. Obviously when this method is called from UI thread it will deadlock.
Here is a repro:
public static string Test(string text)
{
return Task.Run(() =>
{
App.Current.Dispatcher.Invoke(() => { } );
return text + text;
}).Result;
}
void Button_Click(object sender, RoutedEventArgs e) => Test();
I've read multiple questions and like 10 answers of #StephenCleary (even some blogs linked from those), yet I fail to understand how to achieve following:
have a static method, which is easy to call and obtain result from anywhere (e.g. UI event handlers, tasks);
this method should block the caller and after it the caller code should continue run in the same context;
this method shouldn't freeze UI.
The closest analogy to what Test() should behave like is MessageBox.Show().
Is it achieve-able?
P.S.: to keep question short I am not attaching my various async/await attempts as well as one working for UI calls, but terrible looking using DoEvents one.
You can not.
Even just 2 of those 3 requirements can't be achieved together - "this method should block the caller" is in conflict with "this method shouldn't freeze UI".
You have to make this method either asynchronous in some way (await, callback) or make it executable in small chunks to block UI only for short periods of time using for example timer to schedule each step.
Just to reiterate what you already know - you can't block thread and call it back at the same time as discusses in many questions like - await works but calling task.Result hangs/deadlocks.
To achieve something what MessageBox does (but without creating window) one can do something like this:
public class Data
{
public object Lock { get; } = new object();
public bool IsFinished { get; set; }
}
public static bool Test(string text)
{
var data = new Data();
Task.Run(() =>
{
Thread.Sleep(1000); // simulate work
App.Current.Dispatcher.Invoke(() => { });
lock (data.Lock)
{
data.IsFinished = true;
Monitor.Pulse(data.Lock); // wake up
}
});
if (App.Current.Dispatcher.CheckAccess())
while (!data.IsFinished)
DoEvents();
else
lock (data.Lock)
Monitor.Wait(data.Lock);
return false;
}
static void DoEvents() // for wpf
{
var frame = new DispatcherFrame();
Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.Background, new Func<object, object>(o =>
{
((DispatcherFrame)o).Continue = false;
return null;
}), frame);
Dispatcher.PushFrame(frame);
}
The idea is simple: check if current thread need invoke (UI thread) and then either run DoEvents loop or block thread.
Test() can be called from UI thread or from another task.
It works (not fully tested though), but it's crappy. I hope this will make my requirements clear and I still need the answer to my question if there is any better "no, you can't do this" ;)
Since the Windows 8 consumer preview was released a few days ago, I am working on the new WinRT (for Metro Applications) in C# and I had ported my self written IRC class to the new threading and networking.
The problem is: My class is running an thread for receiving messages from the server. If this happens, the thread is making some parsing and then firing an event to inform the application about this. The subscribed function then 'should' update the UI (an textblock).
This is the problem, the thread cannot update the UI and the invoker method that has worked with .NET 4.0 doesn't seem to be possible anymore. Is there an new workaround for this or even an better way to update the UI ? If I try to update the UI from the event subscriber i will get this Exception:
The application called an interface that was marshalled for a
different thread (Exception from HRESULT: 0x8001010E
(RPC_E_WRONG_THREAD))
The preferred way to deal with this in WinRT (and C# 5 in general) is to use async-await:
private async void Button_Click(object sender, RoutedEventArgs e)
{
string text = await Task.Run(() => Compute());
this.TextBlock.Text = text;
}
Here, the Compute() method will run on a background thread and when it finishes, the rest of the method will execute on the UI thread. In the meantime, the UI thread is free to do whatever it needs (like processing other events).
But if you don't want to or can't use async, you can use Dispatcher, in a similar (although different) way as in WPF:
private void Button_Click(object sender, RoutedEventArgs e)
{
Task.Run(() => Compute());
}
private void Compute()
{
// perform computation here
Dispatcher.Invoke(CoreDispatcherPriority.Normal, ShowText, this, resultString);
}
private void ShowText(object sender, InvokedHandlerArgs e)
{
this.TextBlock.Text = (string)e.Context;
}
Here is an easier way to do it I think!
First capture your UI SyncronizationContext with the following:
var UISyncContext = TaskScheduler.FromCurrentSynchronizationContext();
Run your server call operation or any other background thread operation you need:
Task serverTask= Task.Run(()=> { /* DoWorkHere(); */} );
Then do your UI operation on the UISyncContext you captured in first step:
Task uiTask= serverTask.ContinueWith((t)=>{TextBlockName.Text="your value"; }, UISyncContext);
IMO I think "ThreadPool" is the recommended route.
https://msdn.microsoft.com/en-us/library/windows/apps/xaml/hh465290.aspx
public static Task InvokeBackground(Func<Task> action)
{
var tcs = new TaskCompletionSource<bool>();
var unused = ThreadPool.RunAsync(async (obj) =>
{
await action();
tcs.TrySetResult(true);
});
return tcs.Task;
}
What's the best way to thread work (methods) in c#?
For example:
Let's say I have a form and want to load data from db.
My form controls:
- dataGridView (to show data from DB),
- label (loading status) and
- button (start loading).
When I click the button my form is frozen until the task is done. Also the loading status does not change until task is done. I think async threading would be the answer?
So my question: what's the best way to handle this? I know there is a lot stuff about Threading, but what's the difference between them and how do you make it thread safe?
How do you solve this kind of problems?
Best Regards.
If using Windows Forms, you should look at BackrgroundWorker. More generally, it is often useful to use the ThreadPool class. And finally, it is worth to take a look at the new .NET 4's Parallel class.
There is no universal 'best' way to thread work. You just have to try different ways of doing things, I'm afraid.
I particularly like Jeremy D. Miller's continuation idea described at this page (scroll down to find the "continuations" section). It's really elegant and means writing very little boilerplate code.
Basically, when you call "ExecuteWithContinuation" with a Func argument, the function is executed asynchronously, then returns an action when it finishes. The action is then marshalled back onto your UI thread to act as a continuation. This allows you to quickly split your operations into two bits:
Perform long running operation that shouldn't block the UI
... when finished, update the UI on the UI thread
It takes a bit of getting used to, but it's pretty cool.
public class AsyncCommandExecutor : ICommandExecutor
{
private readonly SynchronizationContext m_context;
public AsyncCommandExecutor(SynchronizationContext context)
{
if (context == null) throw new ArgumentNullException("context");
m_context = context;
}
public void Execute(Action command)
{
ThreadPool.QueueUserWorkItem(o => command());
}
public void ExecuteWithContinuation(Func<Action> command)
{
ThreadPool.QueueUserWorkItem(o =>
{
var continuation = command();
m_context.Send(x => continuation(), null);
});
}
}
You'd then use it like this (forgive the formatting...)
public void DoSomethingThatTakesAgesAndNeedsToUpdateUiWhenFinished()
{
DisableUi();
m_commandExecutor.ExecuteWithContinuation(
() =>
{
// this is the long-running bit
ConnectToServer();
// This is the continuation that will be run
// on the UI thread
return () =>
{
EnableUi();
};
});
}
You can use this kind of pattern:-
private void RefreshButton_Click(object sender, EventArgs e)
{
MessageLabel.Text = "Working...";
RefreshButton.Enabled = false;
ThreadPool.QueueUserWorkItem(delegate(object state)
{
// do work here
// e.g.
object datasource = GetData();
this.Invoke((Action<object>)delegate(object obj)
{
// gridview should also be accessed in UI thread
// e.g.
MyGridView.DataSource = obj;
MessageLabel.Text = "Done.";
RefreshButton.Enabled = true;
}, datasource);
});
}
You cannot access your controls from the code that runs in the spun-off thread - the framework does not allow this, which explains the error you are getting.
You need to cache the data retrieved from the db in a non-forms object and populate your UI with data from that object after the background worker thread is done (and handle synchronization for access to that object).