I have a windows form with a datagridview and some buttons. One of the buttons when clicked will call a method called loadMyData() that reads some data from a csv and puts them in three datagridviews in the form.
The code is something like this:
public partial class NewForm : Form
{
private void loadData_Click_1(object sender, EventArgs e) // load market data, create a base copy and update gridview
{
ThreadStart thread1Start = new ThreadStart(loadMyData);
Thread t1 = new Thread(thread1Start);
t1.Start();
}
public void loadMyData()
{
dataMap = dataLoader.newLoadTheData(dataMap, grid1, grid2)
}
}
where dataLoader.newLoadTheData is a static method that takes as input my datagridviews (grid1, grid2) and a dictionary (dataMap). The method simply reads some data from a csv and put the numbers in the 2 datagridviews. These are updated from this method and an updated dictionary (dataMap) is also returned by the method. It all works fine when the method loadMyData() is executed normally but I get this error when I execute it as thread:
Cross-thread operation not valid: Control 'grid1' accessed from a
thread other than the thread it was created on.
I realize that I might be using something like "invoke" but I really can't find a clear example that shows how to do this in my case. Can anyone help with tjis situation? How should I change the code to make it work?
When working with your grid from the other thread, you should do something like this:
if (grid1.InvokeRequired)
grid1.Invoke(new Action(() => { /*do my stuff here*/ })
else
{
/*do my stuff here*/
}
You have to marshal the call back to the UI thread.
Are you using WinForms or WPF?
In WPF you can use the Dispatcher.
In WinForms:
Try
// Get the UI thread's context in the constructor.
var context = TaskScheduler.FromCurrentSynchronizationContext();
// Then its possible to start a task directly on the UI thread
var token = Task.Factory.CancellationToken;
Task.Factory.StartNew(() =>
{
this.label1.Text = "Task past first work section...";
}, token, TaskCreationOptions.None, context);
EDIT:
The reason you are getting this error is because you are trying to access the Grid control from another thread. In general, most UI applications are in a STA(Single Threaded Affinity) model, where any interactions with the UI must be done on the "UI" thread which is usually the main/first thread the application starts on.
As you are loading the data on a background thread, after it is finished, you need a way to Marshal(invoke/run) the code which update the Grid on the Main/UI thread.
To achieve this, you create a TaskScheduler on the main thread by using its current SynchronizationContext (as in the constructor of the window/control the current context will be the UI thread) and than later you can pass that context into the Task.Factory.StartNew method as a parameter, so that it knows to "Marshal"(Invoke/Run) the code on the given "Context" which is the UI Thread
System.Threading.Tasks allows you to easily create a child task and run a completion block when all is complete. If you specify the UI context then the completion block will run in the UI thread, no need for Invoke().
Code:
var ui = TaskScheduler.FromCurrentSynchronizationContext();
Task.Factory.StartNew(
() =>
{
// this runs in worker thread
loadMyData();
DoSomeLengthyJob();
DoSomethingElse();
})
.ContinueWith(t =>
{
// now we are in UI thread
// now update the UI with whatever you want
// with the results from your worker thread
dataGridView1.Rows.Add();
}, ui);
Tell me more
Check out this article by Stephen Cleary
Related
What is the best way to run a loop infinitely in parallel with the application?
This is what I have tried so far:
The button that actives the loop:
private void ActiveDeactiveTest(object sender, EventArgs e)
{
active = !active;
}
The loop:
bool runTest = false;
bool active = false;
public async void Test()
{
while (runTest)
{
if (active)
{
LblOutput.Text = "before";
await Task.Delay(1000);
LblOutput.Text = "after";
}
else
{
LblOutput.Text = "Idle";
}
}
}
And the form instantiation:
public MainPage()
{
InitializeComponent();
runTest = true;
Test();
}
Fairly new to this so any help would be appreciated
Thanks in advance
I don't think there is the best way to loop infinitely but I will provide some of the options. Idea is to start another thread from your main application thread and leverage the loops, usually while, to manage indefinite looping.
Be careful if you are developing UI application because they are quite sensitive in terms of background work. For example, WPF allow only access to UI elements only to the thread which create them, thread known as Dispatcher. This mean that every background thread which need to update UI elements needs to delegate work to dispatcher thread. This is also the case with android with the difference that Dispatcher thread is called UI thread (WPF Dispatcher Thread, Android UI Thread)
WPF
In WPF, a DispatcherObject can only be accessed by the Dispatcher it is associated with. For example, a background thread cannot update the contents of a Button that is associated with the Dispatcher on the UI thread. In order for the background thread to access the Content property of the Button, the background thread must delegate the work to the Dispatcher associated with the UI thread. This is accomplished by using either Invoke or BeginInvoke. Invoke is synchronous and BeginInvoke is asynchronous. The operation is added to the queue of the Dispatcher at the specified DispatcherPriority.
Android
The Android UI toolkit is not thread-safe. So, you must not manipulate your UI from a worker thread—you must do all manipulation to your user interface from the UI thread. Thus, there are simply two rules to Android's single thread model:
Do not block the UI thread
Do not access the Android UI toolkit from outside the UI thread
I will provide few examples just as a show case:
//Option A, common one, loop doesn't need to have iteration at all
Task.Factory.StartNew(() =>
{
while (shouldLooping)
{
//do your job
}
});
//Option B, kind of wierd, loop will have at least one iteration
Task.Factory.StartNew(() =>
{
do
{
//do your job
} while (shouldLooping);
});
//Option C, if you are driven by producer/consumer pattern, BlockingCollection should be shared between producer and consumer
Task.Factory.StartNew(() =>
{
foreach (var item in blockingCollection.GetConsumingEnumerable())
{
Console.WriteLine(item);
}
});
What I'm trying to do is perform a heavy task triggered by a button event on the MainWindow, but still be able to drag the window freely. I've tried both the async/await pattern and creating new threads. However, threads will be nonblocking, MainWindow still freezes. Here's the code:
uiTIN.Click += async (o, e) =>
{
var _ = await Task.Run(() => job());
};
That's the button callback and here is the func:
private int job()
{
this.Dispatcher.Invoke(() =>
{
//Other function calls here omitted
});
return 0;
}
EDIT: The workaround was to use BackgroundWorker and I have also decorated dependent UI code snippets in Dispatcher Invoke function
From Microsoft's doccumentation on Dispatcher (emphasis mine):
In WPF, a DispatcherObject can only be accessed by the Dispatcher it is associated with. For example, a background thread cannot update the contents of a Button that is associated with the Dispatcher on the UI thread. In order for the background thread to access the Content property of the Button, the background thread must delegate the work to the Dispatcher associated with the UI thread. This is accomplished by using either Invoke or BeginInvoke. Invoke is synchronous and BeginInvoke is asynchronous. The operation is added to the queue of the Dispatcher at the specified DispatcherPriority.
So basically what you're doing is call an asynchronous method, and then forcing it to run on the UI thread, which accomplishes nothing.
In your //Other function calls here omitted, I'm asuming that you need to access some part of the UI, if that's not the case, all you have to do is remove the Dispatcher.Invoke from your method.
If my assumptions are right, then you must figure out a way of splitting your function, so that the part that isn't UI related run in a Background thread and only what needs to run on the UI Thread actually do.
My suggestion is to use a Background Worker. Here's how it'd look:
uiTIN.Click += (o, e) =>
{
job();
};
... and then ...
private int job()
{
BackgroundWorker worker = new BackgroundWorker();
worker.DoWork += (s, e) =>
{
// Part of other function calls here omitted that don't need to run on the UI thread
Dispatcher.Invoke(() =>
{
// Part of other function calls here omitted that must run on the UI thread
});
};
worker.RunWorkerAsync();
return 0;
}
The normal practice is that you have to return from buttons onClick event callback as soon as you can in order to avoid blocking the main thread(or some refer to UI thread). If the main thread is blocked the application will look like frozen. This is a fundamental design of any GUI application to synchronize UI flow.
You start an async task in callback but you also wait for the task to finish before returning. You should start a BackgroundWorker in the onClick event then return.
It has been explained quite well already why your code was blocking the UI thread (queuing your work on the Dispatcher). But I would not recommend the usage of the BackgroundWorker, I would rather fix your code with Task.Run for several reasons all explained in this article: https://blog.stephencleary.com/2013/09/taskrun-vs-backgroundworker-conclusion.html
I have a worker thread which calculates data for DataGrid after every change user have made. In some cases user make changes too fast so on GUI thread i call
Thread.Abort();
in the meantime on the worker thread i use such a construction
while (true)
{
try
{
_calculateEvent.WaitOne();
...
Application.Current.MainWindow.Dispatcher.BeginInvoke((Action)delegate()
{
_viewModel.UpdateInterfaceFromAssigningInfo(assigningInfo);
});
}
catch (ThreadAbortException)
{
Thread.ResetAbort();
}
}
Don't know if it will work at all, but for now my main problem is i can't call code on the GUI thread to update interface. At Invoke row i have exception: InvalidOperationException with message
The calling thread cannot access this object because a different
thread owns it.
I'm usually use slightly different way:
Application.Current.MainWindow.Dispatcher.BeginInvoke(new Action( ()=>
{
_viewModel.UpdateInterfaceFromAssigningInfo(assigningInfo);
}));
Try, may be it's a reason.
After some researches i have found info that accurately fills my needs, because in my case i need to update UI only after all calculation in Task have been done.
Option 1.
For the case of WPF application we can benefit from synchronization context task scheduler which runs task right on GUI thread. So, one can employ such scenario to update GUI after task has finished:
Task t = Task.Run(() => foo());
t.ContinueWith((task) =>
{
// Update observable properties
}, TaskScheduler.FromCurrentSynchronizationContext());
Continuation task will be executed on GUI thread and so will be able to update GUI.
Option 2
private async void DownloadFileButton_Click(object sender, EventArgs e)
{
// Since we asynchronously wait, the UI thread is not blocked by our code.
await foo();
// Since we resume on the UI context, we can directly access UI elements.
UpdateObservableProperties();
}
In WinForms application I start worker thread that adds data to root a XElement.
Then in main thread I need to wait while worker thread finishes it's work (to get complete XElement), and output this XElement to a textarea.
If I call .Join() on the main thread - it freezes until another thread stops (and user can't click any button on the main form).
Is it possible to unblock main thread while waiting for another thread to finish it's work??
I've tried:
1.
BeginInvoke(new Action(() => {
XmlManager.whReady.WaitOne();
xDocString = xResultDoc.ToString();
}));
2.
string xDocString = String.Empty;
new Thread(() => { xDocString = XelementToString(); }).Start();
txtXmlTree.Text = xDocString;
public string XelementToString() {
XmlManager.whReady.WaitOne();
return xResultDoc.ToString();
}
But it had no effect.
EventWaitHandle XmlManager.whReady.WaitOne(); is being .Set() in the worker thread just before it closes.
Yes, you can use async/await
string docString = await Task.Run(() => {
XmlManager.whReady.WaitOne();
return xResultDoc.ToString();
}).ConfigureAwait(true);
//Execution flow will resume here once the thread is done.
....
//Now do something here with the text (e.g. display it).
...
For example, if you want to run this on a button click, you would have (note the async modifier):
private async void button1_Click(object sender, EventArgs e)
{
...The code above goes here...
}
As to why your code is not working as expected (both of your attempts):
Your code is blocking, because it causes the action to be executed on the thread on which the main form's handle was created (so the UI thread). You typically call BeginInvoke from another (non UI) thread to tell the UI to do something.
You start a thread and then immediately expect to have xDocString ready to use. It doesn't work that way. By the time this line is executed txtXmlTree.Text = xDocString; the thread may or may not have finished executing (most likely not finished).
I hope now you see why await is a way better option. You don't have to synchronize your workers with the main thread, you don't have worry about context switching and making sure UI code executes on the UI thread.
You can use BackgroundWorker class since it's a WinForm application.
The BackgroundWorker will let the sub-task to be run in the background, and notify the main form on it's completion (as well as progress, if needed), so the main form will be able to display the output in the text area once the sub-task is complete.
In my wpf application I have added a piece of code in the button click as below:
private void btn_convert_Click(object sender, RoutedEventArgs e)
{
Thread t = new Thread(new ThreadStart(WorkerMethod));
t.SetApartmentState(ApartmentState.MTA);
t.IsBackground = true;
t.Start();
}
Inside my WorkerMethod() method I have some code like the line below:
btn_convert.Content = "Convert";
When it reaches to this line it throws the exception as the calling thread cannot access this object because a different thread owns it.
I dont want to use Dispatcher as it freezes the UI.. UI should be responsive so I have not opted for Dispatcher invoke or BeginInvoke.
Please give me your valuable suggestions.
I dont want to use Dispatcher as it freezes the UI.. UI should be responsive so i am not opted for Dispatcher invoke or BrginInvoke.
That just shows that you've used the dispatcher badly.
You must access the UI from the UI thread. That doesn't mean your whole WorkerMethod needs to run on the UI thread, but this line:
btn_convert.Content = "Convert";
definitely does. So you might want to keep your current code for starting a thread (do you really need to set the apartment state though) but change any code accessing the UI to use the dispatcher. For example:
Action setButtonContentAction = () => btn_convert.Content = "Convert";
Dispatcher.BeginInvoke(setButtonContentAction);
Alternatively, depending on what your WorkerThread is doing - and if you're using C# 5 - you might want to use the new async features. That can make it easier to keep UI work on the UI thread, but it does depend on what else is going on.
UI changes can only be applied by the main thread. You can check if the main thread call is necessary:
if (btn_convert.InvokeRequired)
{
btn_convert.Invoke((MethodInvoker)(() => btn_convert.Content = "Convert"));
}