I have a multi-threaded application created on top of C#.
In my application i have 1 form with two panels.
pnlSearch
pnlSlide
Each one is controlled by each Thread:
TH_Slide
TH_Search
TH_Slide is called on the Form_OnLoad where i can display all information of my objects inside the pnlSlide.
while(true)
{
// do some infinite work to slide objects information.
Thread.Sleep(20000); // display for 20 secs.
}
The pnlSearch is hidden and will show up when TH_Search is called this is handled when the mouse moved inside the form. And inside the pnlSearch there is txtFilter textBox and a UserControl object as a holder to display the searched information dynamically.
Now the problem is when i type in the txtFilter of a pnlSearch and load the information from my database and display it to the UserControl.
The TH_Slide.ThreadState becomes WaitSleepJoin and would not running anymore hence the slide information in pnlSlide wont make any changes.
NOTE: Cross Thread Operation is handled using delegates therefore it would not throw any errors.
My application is now perfectly working by setting the
TH_Slide.IsBackground = true
TH_Slide.Priority = ThreadPriority.Highest
TH_Search.Priority = ThreadPriority.Normal
I am new to multi-threading programming and i can't fully understand why this algorithm solves my problem.
Related
In my form_load I have a mysql connection that loads data in a datagrid, when starting my application it will first try to load the mysql database and after its done it will load the form/interface with all the controls.
This leaves the user the impression nothing is happening, how can I first load the form/interface with all the controls and then my mysql database?
I would say, 3 approaches
1 - Separate Splash screen
2 - On-Form "splash screen" - first thing you do on form.load, is Me.Show(), then show a message, something like "Please wait while data is loaded". Progress can be shown to users as well using Application.DoEvents which will refresh your screen.
3 - Background worker thread. Using it, you can actually give user progress updates. That will leave your form completely responsive. http://msdn.microsoft.com/en-us/library/cc221403%28v=vs.95%29.aspx
Move your code to Shown event or OnShown overridden method. this will allow your form shown to user before it loads grid.
You should move your "Database calls" to worker threads as well.
protected override void OnShown(EventArgs e)
{
//Delegate the DB calls to worker threads here
//everything will be smooth then..
base.OnShown(e);
}
There are two ways to approach this situation. You need to pick the approach that best suits your needs.
Approach 1: Create a splash screen. A splash screen is a small window that opens up and usually had product info and a message like "Initializing...". You can see this in a lot of applications, such as Microsoft Office. You can see how to make one by looking at this article.
Approach 2: You will need to make your load data happen in a separate thread then your UI. You will need to load your data in the separate thread and bind it to a list of some sort temporarily, then after its done, bind the list to your Datagrid
Use other thread for loading data from mysql. It's loading form first, but not displaying it until code has finished work.
new Thread(new ThreadStart(LoadDataMethodHere)).Start();
You can use Form Shown event.
Alternatively you can use a different thread to connect to database and show data or use a timer.
I'm working on a simulation code where I have a "Project" which can hold numerous simulations. You can choose to run them one at a time, or you can run them all in sequence. In this specific case, I have 18 simulations which run one by one, and overall the process takes about 20 sec.
In short, a button is pressed on a form, and the following actions occur:
1) Create simulation object
2) Perform simulation start command
3) Write simulation data to file
4) Dispose simulation object
5) Update DataGridView which holds the simulation list (rewrites "Processing" to "Complete")
6) Update progress bar value in user control.
7) Refresh user control.
Rough source code is as follows:
for (int i = 0; i < dataSet.Count; i++)
{
using (Processor p = new Processor())
{
bool didTestPass = p.RunTest(dataSet[i]);
if (didTestPass)
dataGridViewProcessList.Rows[i].Cells[5].Value = "Run complete.";
else
dataGridViewProcessList.Rows[i].Cells[5].Value = "Run completed with errors.";
}
progressBarRuntime.Value = ((i+1) / dataSet.Count) * 100;
this.Refresh();
this.OnUpdateMainForm(this, null);
}
What I've found is, if you remain within the application's focus, all 18 simulations run fine. However, if you drop focus (say, switch to another program), it consistently behaves erratically at the 8th simulation. I say erratically because it acts differently:
When debugging through Visual Studio, the form freezes briefly, then suddenly all remaining simulations are processed and the progress bar snaps to full.
When running as a standalone program, it crashes straight to desktop. No warning, no exception throw, nothing.
I've also found that if I stay focused and let it reach, say, simulation 14, then drop focus from the program, it will immediately exhibit the above behavior.
I'm not particularly familiar with the concept of performing large calculation efforts under the hood while a Windows Form is active. At first I felt like maybe the Form needed to be refreshed (since this is all happening on a UserControl) but I saw no difference when I put in an event to force the Form to Refresh().
I ended up discovering that the source of my problem was that I was performing work on the interface's main thread, and as a result it was causing my program to hang and crash to desktop.
I created a BackgroundWorker and placed my work code into the DoWork event, then moved the ProgressBar indicator update into the ProgressChanged event.
More details regarding BackgroundWorker and its implementation at MSDN.
In short, the lesson learned for me is that if an activity in a Form is going to take longer than a few seconds, it should be done using a BackgroundWorker to prevent hanging up the interface.
In a WPF 4 app, I have a very big user control full of controls which takes something like 4s to initialize on a fast machine. During this time the application is of course not responsive at all.
Is there a way to show an animation in the main window while this control is initialized?
I understand that I cannot create it on another thread. But is there maybe a way to create it with a lower priority from the Dispatcher so that I could show a spinning wheel or so on the main window which would still spin?
(The only solution I can think of right now would be to break the user control into further pieces and load them only when needed. But this will take a lot of development time to change.)
Update1
To be more clear: It is a simple WPF window using tab pages. When a new tab page is opened I'm initializing the user control which holds the controls for this tab page. One of these user controls is so full of controls that it takes 4s until the new tab page will be shown.
So I thought showing a spinning wheel would be better than having a blocked application.
I think you are going to have to break this user control up into pieces. What you can do is use a BackgroundWorker to coordinate the 'building' of this user control. Each time the DoWork event is fired, use Dispatcher.BeginInvoke to create an add the next control to your UI. This technique is described in the following blog post:
http://loekvandenouweland.com/index.php/2010/12/wp7-add-user-controls-graphics-in-background-thread/
This will allow you to show an animation during loading.
Why can't you initialize it on another thread? I see two scenarios:
Initialization is slow for non-WPF reasons that be preloaded/precomputed on another thread before entering the core WPF initialization.
WPF itself is consuming 4 seconds of CPU time (though, that's really WTF levels of CPU time...). If so, you can start another STA thread with it's own message pump that can display an independant UI (e.g: a spinning wheel) until the primary thread is done loading.
You can create "dialogs" which implicitly create a new Dispatcher and run on a background thread, or you can explicitly create your own Dispatcher (=message pump).
I use the following method to do so:
public static Dispatcher StartNewDispatcher(ThreadPriority cpuPriority = ThreadPriority.Normal) {
using (var sem = new SemaphoreSlim(0)) {
Dispatcher retval = null;
var winThread = new Thread(() => {
retval = Dispatcher.CurrentDispatcher;
sem.Release();
Dispatcher.Run();
}) { IsBackground = true, Priority = cpuPriority };
winThread.SetApartmentState(ApartmentState.STA);
winThread.Start();
sem.Wait();
return retval;
}
}
This gives you real multi-threaded UI; but it also means you can't databind or in any other way directly communicate between the two UI's: after all, WPF objects have thread-affinity.
Before you go this route, verify there aren't any slow components you can preload using a profiler: option 1 (preload heavy stuff before WPF init) is simpler and cleaner.
A solution could be to move the slow part (loading data? that doesn't belong IN the control) to another thread as you mentioned.
Or use a VirtualizingStackPanel to lazy load what is needed.
Could you elaborate on the reason for the delay?
I am trying to use a background worker in order to retrieve a large amount of data from the database without stalling the main thread. This seems to work well, except that when it comes to update the UI, the update freezes the screen. Relevant code as follows:
private void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
lvwTest.BeginUpdate();
lvwTest.Items.Clear();
// Populate the UI
foreach (TestItem ti in testData)
{
ListViewItem lvi = lvwTest.Items.Add(ti.Value1);
lvi.SubItems.Add(ti.Value2);
}
lvwTest.EndUpdate();
}
The update takes around 2 - 3 seconds, for which time the screen is locked. I realise that only the main thread can update the screen, but is it possible to load this data into memory in some way (in a background thread or another instance of a listview or something) and then just display it? All I want to happen is for the program to simply refresh the data without taking up time in the main thread.
I recommend loading the data into memory and using a virtual mode ListView. That way, you only create the ListViewItem objects as they are needed.
If you have to load really huge amount of data into UI it will require time and it will block our app. The option is smart scrolling or pagination. You load all data but you put it piece by piece upon user request.
Since most of the above are good advice, but don't really solve your immediate problem, here is another approach:
This will update your GUI and keep it responsive. Assuming you are in WinForm App?
Application.DoEvents();
this.Refresh();
Nevertheless, this does not mean that maybe you should not listen to the ideas from above :-)
In addition to virtualization, I would recommend breaking the items into batches of, say, 100 and adding each batch in its own message. That way, the UI has a change to process other messages whilst the batches are being added to the ListView.
In other words, all the RunWorkerCompleted handler does is queue the first batch for adding in a separate message. The adding method will then add the items and then queue the next batch. This will continue until there are not more items left to add. At that point, you would re-enable the relevant portion of your UI (the ListView).
I've got a Windows Forms (C#) project with multiple comboboxes/listboxes etc that are populated when the form loads.
The problem is that the loading of the comboboxes/listboxes is slow, and since the loading is done when the form is trying to display the entire form isn't shown until all the controls have been populated. This can in some circumstances be 20+ seconds.
Had there been a Form_finished_loaded type of event I could have put my code in there, but I can't find an event that is fired after the form is done drawing the basic controls.
I have one requirement though - the loading has to be done in the main thread (since I get the items from a non-threading friendly COM-application).
I have found one potential solution, but perhaps there is a better way?
I can create a System.Timer.Timer when creating the form, and have the first Tick be called about 1 second later, and then populate the lists from that tick. That gives the form enough time to be displayed before it starts filling the lists.
Does anyone have any other tips on how to delay the loading of the controls?
There is the Shown event that "occurs whenever the form is first displayed.". Also you may want to use the BeginUpdate and EndUpdate functions to make the populating of your combobox faster.
It has that certain smell of workaround, but this approach should fulfil your needs:
private bool _hasInitialized = false;
private void Form1_Shown(object sender, EventArgs e)
{
if (!_hasInitialized)
{
ThreadPool.QueueUserWorkItem(state =>
{
Thread.Sleep(200); // brief sleep to allow the main thread
// to paint the form nicely
this.Invoke((Action)delegate { LoadData(); });
});
}
}
private void LoadData()
{
// do the data loading
_hasInitialized = true;
}
What it does is that it reacts when the form is shown, checks if it has already been initialized before, and if not it spawns a thread that will wait for a brief moment before calling the LoadData method on the main thread. This will allow for the form to get painted properly. The samething could perhaps be achieve by simply calling this.Refresh() but I like the idea of letting the system decide how to do the work.
I would still try to push the data loading onto a worker thread, invoking back on the main thread for populating the UI (if it is at all possible with the COM component).
Can you get your data from a web service that calls the COM component?
That way, you can display empty controls on a Locked form at the start, make Asynchronous calls to get the data, and on return populate the respective combos, and once all of them are loaded, you can unlock the form for the user to use.
You could listen for the VisibleChanged event and the first time it's value is true you put your initialization code.
Isn't FormShown the event you're looking for?
When you say that you cannot use a background thread because of COM what do you mean? I am using many COM components within my apps and running them on background threads.
If you create a new thread as an STAThread you can probably load the ComboBox/ListBox on a Non-UI thread. IIRC the ThreadPool allocates worker threads as MTAThread so you'll need to actually create a thread manually instead of using ThreadPool.QueueUserWorkItem.