I am working on windows app which generates various results of testing. The UI provides a button Results which has various methods as event handlers. These methods are provided by an API module. The module fetches the data from warehouse and then displays it to the user.
The API call is asynchronous.
But UI gets blocked once the button is clicked.
After quite some readings , I came to know that Dispatcher helps in running a process in background . Dispatcher can be invoked/call only by the UI thread.
How can I implement dispatcher to keep the UI away from being blocked?
The function in c# looks something like this
private async void get_results(object sender, RoutedEventArgs e)
{
List<resultsummary> data = new List<resultsummary>();
if(id==plan_id)
{
data= await getdata.getsummary(id, name);
}
}
Edit:-
This is my understanding of async and await in the above code. But it gives an error cannot await. The getdata class is in differnet namespace and the method getsummary is defined in that space.
The main objective is to unblock the UI and how to go about this using Dispatcher technique?
You don't need to use a Dispatcher to do what you want. In order to fetch data on a background thread and then pass that data from the background thread to the UI thread, you can use the Task class.
Task.Factory.StartNew((Func<YourDataType>)delegate()
{
// Fetch data on background thread here (return YourDataType, whatever that is)
return DataAccessClass.GetData();
}).ContinueWith((Task<YourDataType> task) =>
{
// Update controls with result on UI thread here
YourUiProperty = task.Result;
}, TaskScheduler.FromCurrentSynchronizationContext());
Obviously, you'll need to replace the YourDataType type with whatever your data type is.
You can use Action...
Put your code in an Action like this"
Action action = () =>
{
// your code...
};
assume that you have a label to show result
myLabel.Dispatcher.BeginInvoke(action);
Related
i am using the following function plot() to plot data on google maps.now when i am calling the same function from another button click event it is not getting executed rather going into else() statement.my plot function is as follows:
public void plot(double temp_lat, double temp_long, string temp_date, string temp_time, string temp_bty_value)
{
if (this.InvokeRequired)
{
// do something
}
else { this.Close(); }
}
i am calling this function from button click event as follows:
private void button6_Click_1(object sender, EventArgs e) /// refresh button
{
this.Invoke((MethodInvoker)(() =>
{
plot(28.5231445, 77.40388525, "17/06/20", "17:00:50", "82");
}));
}
when is the reason for this? i am new to invoking methods.please help
Edit:https://stackoverflow.com/a/43797637/6412780 The reason why i am using invocation because i was plotting 5 markers simultaneously on gmap from different threads.so for synchronization i am using invocation method.BUT now i want to update my data. i made a refresh button which contains new lat /long (passing manually here) to plot on the map.being on the same UI i called the plot() function directly ,but even then i wasn't able to execute the if statement. that is what i am doing .
In WinForms all1 UI operations – anything accessing any member of any control instance – need to be performed on the single thread executing UI operations.
Invoke can be used by a non-UI thread to cause the UI thread to execute code (with BeginInvoke this can be concurrently).
But an event handler for a control will always be running on the UI thread when triggered by the UI. So no need to switch threads. To run code on a different thread (eg. it would block the UI thread) there are various options, these days using async/await, to let the runtime work it out, is preferred.
In the question's code the event handler is already using Invoke which is pointless: it is already on the UI thread.
1 Simplifying here, the actual rules have subtleties for advanced use cases.
Invocation is required if you try to access elements, that are exclusive to one Threadfrom another Thread. It is very common when accessing GUI elements from a background thread. Here is an example:
Thread t = new Thread(() => plot(28.5231445, 77.40388525, "17/06/20", "17:00:50", "82"));
public void plot(double temp_lat, double temp_long, string temp_date, string temp_time, string temp_bty_value)
{
if (this.InvokeRequired)
{
this.Invoke((MethodInvoker)(() =>
{
this.Close();
}));
}
else {
this.Close();
}
}
thisseems to be a Form. If you call the Close method from another Thread you will most likely get an Exception (There are ways to prevent this, but thats not considered good style). You need to invoke that method (let the Form decide, when it is ready to execute the command)
I have successfully built a plugin mechanism where I can create UI controls in a separate AppDomain and display them as part of a Form in the main AppDomain.
These UI controls do their own data loading so when I open a form about 10 different plugins get created and each needs to load its data.
Again this all works fine if I do it synchronously but I would like to use the async/await pattern in each plugin. My refresh method looks like this:
protected async void RefreshData()
{
_data = await LoadAsync(_taskId); <= UI Thread :)
OnDataChanged(); <= Worker Thread :(
}
Now here starts the problem. When I enter this method I am on the main UI thread. But when the await is over I am on a worker thread so I get a cross thread exception in the OnDataChanged() method which updates the controls.
await should by default use the SynchronizationContext.Current for its continuation but since I am in an other AppDomain this happens to be NULL.
So my question is. How can I configure await to continue on the current thread, that is the UI thread?
I know I can grab a control and do Invoke() but I am also using the MVVM pattern and this is in the View Model so I don't have access to any controls there and all View Model -> View communications are done through data bindings.
I finally figured out how to get back to the UI-Thread from within a separate AppDomain, without having a handle to a control.
Since my view model is always instantiated on the UI thread, I simply grab the current dispatcher:
_dispatcher = System.Windows.Threading.Dispatcher.CurrentDispatcher
Now in my RefreshData method all I have to do is dispatch the action I want to perform after the await.
protected async void RefreshData()
{
_data = await LoadAsync(_taskId); <= UI Thread :)
_dispatcher.Invoke(() => OnDataChanged()); <= UI Thread :)
}
This can of course be made more fancy, by encapsulating the dispatcher, etc.
The idea for this actually came from the: MVVM Light Toolkit
I am developping a WPF application.
I have a couple of timers that work asynchronously and they are created with System.Threading.Timer objects.
new System.Threading.Timer(
new System.Threading.TimerCallback(Timer_Function),
null,
timing,
timing
);
Timer_Function()
{
/*code that runs asynchronously */
}
In order to update the controls that are in WPF view I use delegate methods called by Dispatcher.Invoke otherwise the compiler would raise InvalidOperationException (The calling thread cannot access this object because a different thread owns it.)
My problem is that the delegate method do not run asynchronously and i have to do a lot of work in it so the application stops.
What can i do to run delegate method asynchronously?
What you need to do is separate out your business logic for determining what you should display, from the code that displays the results of those calculations to the UI. The business logic, the non-UI code, should be run in a non-UI thread and compute some result. That result should then be given to a method that will marshal to the UI thread and then display those results.
If you need your program to continue executing when the Timer_Function() code is run you could use a Task.Run to perform the work on a worker thread.
As always with parallel execution you need to beware of synchronization issues. Make sure you don't need the results before the work is processed and dispathed. Using the dispatcher as you mentioned might be enough, just make sure you think it through.
Something like:
Timer_Function()
{
Task.Run(() =>
{
/* do your work here */
});
}
Sorry if I misunderstood your issue
For asynchronous tasks, use this -
Task.Factory.StartNew(() =>
{
// Code which needs to run asynchronously
}.ContinueWith(task =>
{
Application.Current.Dispatcher.Invoke(new Action(() =>
{
// Code in which you are updating UI
});
}
While maintaining some code, I discovered that we have an infinite hang-up in a background worker. The worker requires access to a script file. The original code was written to pop up a file dialog if no script file was defined, to allow the user to select one. It looks something like this:
private void bgworker_DoWork(object sender, DoWorkEventArgs e)
{
... snip ...
if (String.IsNullOrWhitespace(scriptFile))
{
scriptFile = PromptForScript();
}
... snip ...
}
private string PrompForScript()
{
string script = "";
OpenFileDialog openDialog = new OpenFileDialog();
if (openDialog.ShowDialog() == DialogResult.OK)
{
script = openDialog.FileName;
}
return script;
}
I've read up a bit about MethodInvoker, but almost all of the invoke methods require that you call them from a control. The background worker in question is running from a separate class, which doesn't extend Control. Do I use the form that calls the class with the bgworker for that? Or is there another way of interrupting the thread for user input?
It's not recommended to invoke the UI from the background worker DoWork event handler. BackgroundWorker is meant to do work on a non-UI thread to keep the UI responsive. You should ask for any file information before starting the BackgroundWorker object with RunWorkerAsync.
What you want to do is capture the SynchronizationContext on the UI thread and pass that along to the background worker. The BackgroundWorker can call Send() (synchronous, like Invoke) and Post() (asynchronous, like BeginInvoke) on the context to invoke back to the correct UI thread. That said, there is probably no need for the BackgroundWorker in this case- a regular threadpool thread would do just fine.
This (slightly modified) block of code from http://msmvps.com/blogs/manoj/archive/2005/11/03/74120.aspx should give you the general idea:
private void button1_Click(object sender, EventArgs e)
{
// Here we are on the UI thread, so SynchronizationContext.Current
// is going to be a WindowsFormsSynchronizationContext that Invokes properly
ctx = SynchronizationContext.Current;
ThreadPool.QueueUserWorkItem(
// This delegate is going to be invoked on a background thread
s => {
// This uses the context captured above to invoke
// back to the UI without the "messy" referencing
// of a particular form
ctx.Send(s2 =>
{
// Interact with your UI here- you are on the UI thread
},null);
}
);
}
If some Form kicks off a long-running process within another class that uses a BGworker, why wouldn't the form (or presenter, depending on UI architecture) handle the processing of the error state?
Perhaps, just pass back some status result (or throw a very targeted, specific exception that you can handle in the UI)?
Leave the background worker to determine if there IS an error, but leave handing the error (especially the UI portion of showing a message box) to the upper layers.
Sorry this didn't have more concrete code but it could go a lot of different ways depending on how your system is architected.
Well, the Form class has an Invoke method, so passing the form instance to the background working class should work.
Background: I used to call a stored procedure during my Form Load. However, since this resulted in a suboptimal UI experience, I put my SP call in a task of its own inside the Shown event. Since this is typically the last event in the form display process, it resulted in a much better experience than putting stuff in the Form load event. I have:
private void MainForm_Shown(object sender, EventArgs e)
{
dbCheckTask = Task<bool>.Factory.StartNew(RunSPAndCheckStuff());
// both of below run on the GUI thread.
// if I print the thread ID in mycallback it is the GUI thread id
dbCheckTask.ContinueWith(mycallback());
// I also tried below. But obviously, that too runs on the GUI thread
mycallback(dbCheckTask.Result)
}
Because they fire on the GUI thread, my startup form paint is still neither instantaneous nor smooth. How can I get my task complete callback on a non-GUI thread without resorting to events? Whenever the task completes and if something is wrong and only if something is wrong (bool result returned false) then the user gets a message box pop. Until then he could go ahead and do other non database related stuff on the form. Please advise how I can get a task completion callback with task result in a non gui thread. Thank you
All this stuff is addressed best in the Async language extensions you can download here and has the homepage here.
It introduces the async and await keywords to C# and VB that will let you write code that switches back and forth between UI and background threads effortlessly even within a single method. The compiler will convert that to tasks, continuations, error catching etc etc transparantly without you having to worry about any of that. The example that would interest you would be this one:
public async void AsyncSwitchToCPU() {
Console.WriteLine("On the UI thread.");
// Switch to a thread pool thread:
await new SynchronizationContext().SwitchTo();
Console.WriteLine("Starting CPU-intensive work on background thread...");
int result = DoCpuIntensiveWork();
Console.WriteLine("Done with CPU-intensive work!");
// Switch back to UI thread
await Application.Current.Dispatcher.SwitchTo();
Console.WriteLine("Back on the UI thread. Result is {0}.", result);
}
public int DoCpuIntensiveWork()
{
// Simulate some CPU-bound work on the background thread:
Thread.Sleep(5000);
return 123;
}
This even has a go-live license (with some reservations from MS). Very elegant stuff borrowed from F#.
Rgds Gert-Jan
I'd use a BackgroundWorker for this, personally. One way to get your callback to run on the task thread would be to modify your method call and task creation as follows:
private void MainForm_Shown(object sender, EventArgs e)
{
dbCheckTask = Task<bool>.Factory.StartNew(() => RunSPAndCheckStuff(mycallback));
...
}
private bool RunSPAndCheckStuff(Action<bool> callback)
{
bool result = false;
// Do stuff
callback(result);
return result;
}
You should look into using the Asynchronous API's rather than calling the synchronous versions in a background thread:
http://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqlcommand.beginexecutenonquery.aspx
The advantage to that is that no thread will be blocked, and I believe the callback will be called on ThreadPool thread, e.g. NOT on the GUI thread. From there you can marshal any GUI calls back to the GUI thread with Invoke/BeginInvoke.
Why not doing:
Task.Factory.StartNew(()=>WorkerMethod());
And define WorkerMethod() as:
void WorkerMethod()
{
RunSPAndCheckStuff(); // this blocks until finished
DoSomeMoreStuff(); // then this continuous
}
Otherwise please provide more details on what do you want to accomplish.