Loading data in a thread [duplicate] - c#

This question already has answers here:
Cross-thread operation not valid: Control accessed from a thread other than the thread it was created on
(22 answers)
Closed 9 years ago.
My problem is simply that my SQL query takes over 2 minutes to complete and I can't have my application freezing up while it tries to get all that data. I've tried multi-threading but I keep running into an error that I am pretty sure you'll recognize. My code is under that.
Cross-thread operation not valid: Control 'labelEdit1' accessed from a thread other than the thread it was created on.
private void Form_Load(object sender, EventArgs e)
{
startup = new Thread(loadInThread);
startup.Start();
}
private void loadInThread()
{
//getsDataFromSQL() is the method that takes over 2 minutes to do
//it returns a String Array if that is helpful
comboEdit1.Properties.Items.AddRange(getsDataFromSQL());
startup.Abort();
}
If there are better ways to do this then please let me know, all I need is for the application to not freeze up and the data to get loaded into the comboEdit. Also I know the SQL statements could be optimized but that's not what this question is about, so please don't suggest it,.

How are you doing the multithreading? Are you creating the thread manually? Don't do that. I would recommend going with one of the following three approaches:
async/await - http://msdn.microsoft.com/en-us/library/vstudio/hh191443.aspx (.NET 4.5+ only)
The Task Parallel Library - http://msdn.microsoft.com/en-us/library/dd460717.aspx (.NET 4.0+ only)
BackgroundWorker - http://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker.aspx
Handling the thread logic manually is of course also possible. In that case you'll probably want to look into the BeginInvoke method: http://msdn.microsoft.com/en-us/library/system.windows.forms.control.begininvoke.aspx.

You need to use dispatcher here. This is because you cannot access controls owned by UI thread from another thread. Check this link here. I have not checked this for compilation errors but this should do the trick for you with a little modification of course.
private void loadInThread()
{
//getsDataFromSQL() is the method that takes over 2 minutes to do
//it returns a String Array if that is helpful
comboEdit1.Dispatcher.BeginInvoke((Action)(() =>
{
comboEdit1.Properties.Items.AddRange(getsDataFromSQL());
}));
startup.Abort();
}

Related

Can Task.Delay cause thread switching? [duplicate]

This question already has answers here:
I thought await continued on the same thread as the caller, but it seems not to
(3 answers)
Closed 2 years ago.
I have a long running process that sends data to the other machine. But this data is received in chunks (like a set of 100 packets, then delay of minimum 10 seconds).
I started the sending function on a separate thread using
Task.Run(() => { SendPackets(); });
The packets to be sent are queued in a Queue<Packet> object by some other function.
In SendPackets() I am using a while loop to retrieve and send (asynchronously) all items available in the queue.
void SendPackets()
{
while(isRunning)
{
while(thePacketQueue.Count > 0)
{
Packet pkt = thePacketQueue.Dequeue();
BeginSend(pkt, callback); // Actual code to send data over asynchronously
}
Task.Delay(1000); // <---- My question lies here
}
}
All the locks are in place!
My question is, when I use Task.Delay, is it possible then the next cycle may be executed by a thread different from the current one?
Is there any other approach, where instead of specifying delay for 1 second, I can use something like ManualResetEvent, and what would be the respective code (I have no idea how to use the ManualResetEvent etc.
Also, I am new to async/await and TPL, so kindly bear with my ignorance.
TIA.
My question is, when I use Task.Delay, is it possible then the next cycle may be executed by a thread different from the current one?
Not with the code you've got, because that code is buggy. It won't actually delay between cycles at all. It creates a task that will complete in a second, but then ignores that task. Your code should be:
await Task.Delay(1000);
or potentially:
await Task.Delay(1000).ConfigureAwait(false);
With the second piece of code, that can absolutely run each cycle on a different thread. With the first piece of code, it will depend on the synchronization context. If you were running in a synchronization context with thread affinity (e.g. you're calling this from the UI thread of a WPF or WinForms app) then the async method will continue on the same thread after the delay completes. If you're running without a synchronization context, or in a synchronization context that doesn't just use one thread, then again it could run each cycle in a different thread.
As you're starting this code with Task.Run, you won't have a synchronization context - but it's worth being aware that the same piece of code could behave differently when run in a different way.
As a side note, it's not clear what's adding items to thePacketQueue, but unless that's a concurrent collection (e.g. ConcurrentQueue), you may well have a problem there too.

Can an ObservableCollection be changed on a thread outside of main UI thread? [duplicate]

This question already has answers here:
How do I update an ObservableCollection via a worker thread?
(7 answers)
Closed 4 years ago.
I have an application written in C# WPF that has a tree/folder structure which displays an ObservableCollection containing many items.
There is a filter feature where a user can input values and a search will be automatically performed and updated live on the main UI. Similar to where if you are searching something on Google's search engine and an AutoSuggestion box pops out which updates every time you enter a new character or word.
There is a scenario where if the collection has MANY datasets (over 100,000 items) and as soon as i start backspacing the values I've inputted (ex. from after inputting abcd into the textbox to abc to ab from backspacing) the main UI of my application freezes and just completely crashes the program. I figured creating a separate thread to perform this action (turning the method to an async and using await Task.Run() as follows would be the simplest solution but Visual Studio is telling me that a thread outside of the main UI thread cannot make changes to the Observable Collection. After a quick google search it seems like this is actually not suggested so I want to know if there are any other ways to get around this?
Do the search on a separate thread and then re-enter the UI thread to put the result into the collection.
As the error says, you can't modify an ObservableCollection from outside the UI thread.
Fortunately, there is the Dispatcher which allows you to switch context to another thread and run things at will, like so:
Application.Dispatcher.Invoke( () => myCollection.Add(myObj) )
Which will run synchronously from the main/UI thread. To do something asynchronously use BeginInvoke() instead of Invoke()

Dispatcher.CurrentDispatcher.BeginInvoke Not Invoking

I have a FileSystemWatcher and the events raised by this when a watched file changes are raised on a different thread from the UI thread. To avoid and cross-thread acess volation fun, I am attempting to use
public void RaisePathChanged(object sender, RenamedEventArgs e)
{
Dispatcher.CurrentDispatcher.BeginInvoke(new Action(() =>
{
// Some code to handle the file state change here.
}));
}
This compiles fine and the RaisePathChanged is fired as it should be. However, the code inside the delegate Action(() => { /*Here*/ }) never gets called/invoked, the code is merely skipped.
Why is the code being skipped, how can I fix it and is this the best way to insure code is run on the thread that created it in WPF?
Thanks for your time.
You are mixing up things.
Dispatcher.CurrentDispatcher is not the same as Application.Current.Dispatcher.
The second one is the one you seem to be looking for.
Take a look at this.
Dispatcher.CurrentDispatcher vs. Application.Current.Dispatcher
Try it out with application dispatcher.
Dispatcher.CurrentDispatcher is the dispatcher of the "current" thread - in this case the thread RaisePathChanged is executing on.
When you say Dispatcher.CurrentDispatcher .NET will create a new dispatcher if there was none.
It will however not Run() said dispatcher!
So when you schedule something on it (with BeginInvoke) it will not actually be executed unless that dispatcher is running.
That likely answers your first question (Why is it not Invoking?)
To avoid cross-thread access violation, you need the dispatcher of the thread that created whatever you are trying to protect and make sure it is a running dispatcher.
If whatever you are trying to protect was created on the default GUI thread, then use Application.Current.Dispatcher like the previous answer says, otherwise you will need to do a bit more explaining and post a bit more code before we can answer your second question. http://www.diranieh.com/NET_WPF/Threading.htm has a pretty short intro into the subject.

How to call a long method in a new thread to keep the UI running in C# [duplicate]

This question already has answers here:
How to handle long running "thread" in WPF?
(2 answers)
Closed 10 years ago.
I have WPF application with a combobox filled with Users, a grid showing some data for the selected User and a button that calls DoTimeSheetReport().
DoTimeSheetReport() does some work and then opens a new window with a SSRS report. Everything works fine but the method takes a long time to complete, mostly because of the report, which means my UI becomes unresponsive. I tried a couple of ways to start a new thread/task but all of them are blocking the UI's thread. I'm probably doing something wrong but I have no idea.
What's the best way to call a long method in order to not block the UI?
EDIT
I changed my code to isolate the time-consuming part.
reportViewer.SetPageSettings(reportConfiguration.PageSettings);
Using a backgroundWorker on this part did it. Thank you for your help.
#LuisQuijada: That worked, post an answer so I can accept it.
using System.Threading;
new Thread(() =>
{
Thread.CurrentThread.IsBackground = true;
/* run your code here */
Console.WriteLine("Hello, world");
}).Start();
In short: what you need to do is to look at how to use async calls.
As a start place you may look at suggested link in your post and/or the MSDN article:
Calling Synchronous Methods Asynchronously
Dispatcher.BeginInvoke Method (DispatcherPriority, Delegate)

C#: Populating a UI using separate threads

I'm trying to make some sense out of an application Ive been handed in order to track down the source of an error. Theres a bit of code (simplified here) which creates four threads which in turn populate list views on the main form. Each method gets data from the database and retrieves graphics from a resource dll in order to directly populate an imagelist and listview.
From what Ive read on here (link) updating UI elements from any thread other than the UI thread should not be done, and yet this appears to work?
Thread t0 = new Thread(new ThreadStart(PopulateListView1));
t0.IsBackground = true;
t0.Start();
Thread t1 = new Thread(new ThreadStart(PopulateListView2));
t1.Start();
Thread t2 = new Thread(new ThreadStart(PopulateListView3));
t2.Start();
Thread t3 = new Thread(new ThreadStart(PopulateListView4));
t3.Start();
The error itself is a System.InvalidOperationException "Image cannot be added to the ImageList." which has me wondering if the above code is linked in some way.
Iis this method of populating the UI recommended and if not what are the possible complications resulting from it?
Update:
I may have given some misinformation by referring to a 'form'. The application is a windows forms application but the code is from a plugin application based on a usercontrol. The threads are created inside an initialise method publically exposed by this control. The listviews etc are also part of this plugin usercontrol.
DO NOT USE threads for that - if you have to do that async, use WOrkItems on a THreadPool. Thread usage in general should be reseved for long running items - a THreadPool or the new .NET 4.0 tasks API are way better suited for that.
UI elements should only be namipulated from the element creation thread. It "works" or not depending on what version of the .net framework you use or what the control really is if you break this.
Within your threads methods, such as DoWork() for the BackgroundWorker class, for example, you will need to instiate a delegate method to populate your UI control. Then, verifying whether your UI control requires to be invoked (InvokeRequired property), then invoking it when it'srequired to.
private delegate IList<MyObject> PopulateUiControl();
private void myThread_DoWork(object sender, DoWorkEventArgs e) {
PopulateUiControl myDelegate = FillUiControl;
while(uiControl.InvokeRequired)
uiControl.Invoke(myDelegate);
}
private IList<MyObject> FillUiControl() {
uiControl.Items = myThreadResultsITems;
}
It is not a precise working code, as I can't take the time to do the research, etc. but this shall put you in the path to succeed.
In the end, I agree with the others, try to avoid such things in the future, as it can get tricky to debug or reveal some strange behaviour. Perhaps .NET 4 has some improvements on the topic as Microsoft has worked hard to make parallelism easy for the use of multicore processors for developers.
As others have stated, you cannot update your UI from any thread other than the one it was created by.
If a thread wants to update the UI, it needs to invoke a method on the UI control on the same thread that created it using BeginInvoke.
PS: I am assuming when you say UI, you mean a WindowsForms and not WPF. I have used the above solution successfully in WinForms.
Update: Here are a couple of articles that explain the concept in-depth:
* Threading in Windows Forms
* WinForms UI Thread Invokes: An In-Depth Review of Invoke/BeginInvoke/InvokeRequred
Also, related question from SO: In WinForms, why can't you update UI controls from other threads?
Never update UI from a worker thread. Program may work sometimes, but this is undefined behavior. Add this line to the program initialization code:
Control.CheckForIllegalCrossThreadCalls = true;
After this every incorrect UI update attempt fails, allowing to fix all errors in the code.
If this is done because getting the values fr the listviews takes time, then get the values in a background worker and then use the main thread to bind the data to the listview.

Categories

Resources