3

My “form1” is just a simple page with buttons that launch different forms which do all the work, the "form1" code for the first four buttons is below.

What I want is for each form to run in a separate thread.

Public Class Main
    Private Sub btnDownLoadStockPrices_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnDownLoadStockPrices.Click
        LoadStocksFromDownloadSite.Visible = True
    End Sub

    Private Sub btnLoadOptionsIntoDatabase_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnLoadOptionsIntoDatabase.Click
        LoadOptionsIntoDatabase.Visible = True
    End Sub

    Private Sub btnVerifyDatabases_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnVerifyDatabases.Click
        VerifyDatabase.Visible = True
    End Sub

    Private Sub btnAnalyzeStock_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnAnalyzeStock.Click
        AnalyzeSingleStock.visible = True
    End Sub
End Class

I’ve found plenty of code examples to have different buttons on a single form run a subroutine in a separate thread, but for some reason I can’t seem to apply it to an entire form.

I think it’s something simple, like I need to tie each new thread as a handle to each forms “load” routine, but I just can’t get it to work. I don’t care about “synchronizing” threads at all, as each form is really a completely different functional program.

Any help would be much appriciated!

Cody Gray - on strike
  • 239,200
  • 50
  • 490
  • 574
David Workman
  • 31
  • 1
  • 1
  • 2

2 Answers2

3

This isn't very common; generally it's best to limit all UI stuff to a single thread. But if you're convinced that you need each form to run on a separate thread, you must take into account the Windows API event handling model. The [over]-simplified version is that each form must have its own message loop to remove event messages from the queue and process them, so if you want to open a form on a new thread, you need to create that message pump.

The easiest way to do that is using the Application.Run method, and let the .NET Framework handle creating that message pump for you. For example:

Dim frm As Form1 = New Form1()
Application.Run(frm)

From looking at the code shown in your question, I can't discern any possible reason why those forms would need to run on separate threads. You can call the Show method of multiple forms so that they will be displayed on the screen at the same time. They won't block each other as long as you don't use the ShowDialog method, which displays each as a modal dialog. This is the way so many applications display multiple toolbox windows and other kinds of forms on the screen at the same time.

If you need to do some type of processor-intensive calculation, you still don't need to run each on a separate thread. Spin up a background thread (the BackgroundWorker class makes this very simple) and update the appropriate form's UI using the Invoke method.

Cody Gray - on strike
  • 239,200
  • 50
  • 490
  • 574
  • Thanks for the help ... the reason I want the different threads is that some of the functions wait till a specific time of day to download "end of day" stock and option data from my data provider. While these tasks are waiting/downloading, I'd like to be able to use the other forms. I suppose I can just put these specific sub-routines on a new thread, but I thought it would be easier for the each form to be on it's own. – David Workman Feb 07 '11 at 14:25
  • @David: Then the latter part of my answer is what you're looking for. There's absolutely no reason to run the forms on separate threads just so that they'll be available at different times. In fact, since you say that you're not even trying to communicate between forms, your task is even easier. Just create a new instance of a form class, and call `frm.Show()`. You can have as many of them on the screen at a time as you want, and interact with each separately. If you want to do downloading on a background thread, that's simple enough too. There are lots of examples online of BackgroundWorker. – Cody Gray - on strike Feb 07 '11 at 14:27
  • Thanks for the quick response. I think what's hanging me up is that all the forms are part of the project and they all get loaded automatically on startup, and I'm just making them "visible" when I click on the button in Form1. I guess I was trying to find a way to have them load only when I click on the button, and do that in a new thread. I will try putting the waiting/downloaded process in BackgroundWorker and see if that makes it so I can use the other forms at the same time. – David Workman Feb 07 '11 at 14:34
  • @David: Why do all the forms get loaded automatically on startup? Do you intend for that to happen? You know that you can load and show a new form object any time that you want via code, right? `Dim frm As Form1 = New Form1() : frm.Show()` Just put something like that into your button's `Click` event handler method. It's still not clear where you got the idea that you need threads. As David's answer suggests, threads do have their purpose, but more often than not, they just add unnecessary complexity. I think this is a case of the latter. – Cody Gray - on strike Feb 07 '11 at 14:37
  • Actually, I can't figure out how to prevent the other forms from loading on startup ... they were created in Visual Studio by using "Project -> Add Windows Form". I do not have any "New Form" commands anywhere, they are just "there" and I'm setting the form's property to "visible" when I want it to show up. – David Workman Feb 07 '11 at 14:45
  • @David: Ah, yes. VB.NET is hiding an implementation detail from you to preserve backwards compatibility with VB 6. There's a default instance of every form class in your project, so that all you have to do is call a method on it. Hans Passant's answer [here](http://stackoverflow.com/questions/4698538/there-is-a-default-instance-of-form-in-vb-net-but-not-in-c-why/4699360#4699360) is excellent in explaining that behavior. In short, there's really no reason to worry about it. The real question is what's wrong with your code as shown? Do you have problems with the UI being unresponsive? – Cody Gray - on strike Feb 07 '11 at 14:48
  • Ok, thanks - that makes sense, I'll look at that link. The issue I have is that I have two different forms for downloading stock and option data from the internet, one of them at various times during the day and then the "end of day" data at around 5pm. While these forms are waiting, downloading, and stuffin the data into the SQL database, all the other forms are hung. – David Workman Feb 07 '11 at 14:52
  • @David: Yeah, then pushing that downloading/updating off into a new `BackgroundWorker` is the best option. In fact, that will keep even the form *being* updated from getting hung. Not that you need that, but it definitely looks bad from a usability perspective if Windows whites out the window and it looks like it's crashed because it's been unresponsive. Like I mentioned before, there are lots of examples around the web and here on SO about using `BackgroundWorker` and the `Invoke` method to do that. Actually, the sample code on MSDN (with the documentation, check my answer) is pretty good. – Cody Gray - on strike Feb 07 '11 at 14:57
  • Excellent, I really appriciate the help. – David Workman Feb 07 '11 at 15:01
  • @Cody - Just an update ... after a few false starts, I have the BackgroundWorker going just fine on two forms and it works like a charm! My main textbox on both forms is just for "output" status from the downloader and database stuffer, it never gets updated from anywhere else, so instead of going through all the hassle of changing dozens of txtOutput.AppendText lines to use the "invoke" routine, I just set the control to "CheckForIllegalCrossThreadCalls=false". I know this isn't the "proper" way to do it, but it works just fine. – David Workman Feb 08 '11 at 13:51
1

You can certainly do this on Win32 but I don't know how well this maps over to .net.

The essential issue is that window handles have thread affinity. So you really need all interaction with them to happen in that thread. Essentially this means that you create all the window handles associated with that form in its thread. You also need to run a message loop in the thread.

The reason that people usually run all the UI out of the main thread and handle long-running actions in separate threads is that it is easier that way. You should ask yourself again why you want to do it this non-standard way.

I suspect you are not quite seeing the full picture. The need for threads in a desktop app principally arises when you have long running actions. Usually you want to keep your UI responsive and providing feedback for the long running action. Doing so leads to threads.

However, in your proposed solution you now have a multitude of extra threads and complexity, and you are still faced with the original problem! A long running action on one of your forms will hang it unless you perform that action in a separate thread, and once again we reach the standard solution to the problem.

David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
  • Thanks David - as noted in my other reply to Cody, I don't care if that one form "hangs" while it is waiting and downloading, but I want to use all the other forms in the meantime. I have never used any sort of multi-threading at all, so I'm not sure what approach is "standard" and what is "non-standard" :-) – David Workman Feb 07 '11 at 14:38
  • 1
    @David I still think it will be much easier to run all your UI out of the main thread and put the long running calculations in separate threads. If you let a UI thread *hang* then the system gets a bit snotty about it and fades out your Window and threatens to kill it because it's not responding. Worker threads for the calcs is probably the easier option too as well as better. – David Heffernan Feb 07 '11 at 14:59
  • Thanks, I appriciate the guidance from you and Cody. I was thinking that it would be a simple matter to just load each form in a new thread, and that way I could program whatever I wanted on each form and not worry about managing anything else. I am going to investigate using the BackgroundWorker for the waiting/downloading/database stuffing as Cody suggests above - that looks like the best option. – David Workman Feb 07 '11 at 15:18