1

I currently have a project that starts up a central logic class (which uses some other .dll's to check on hardware or connect to the database). After that, a WPF form is started. This form uses the information of the central logic.

Currently, the application is being started like this:

public void StartTheWholeBunch()
{
    Thread thread = new Thread(() =>
    {
        applicationLogic = new ApplicationLogic();
        Application app = new Application();
        app.Run(new MainWindow(applicationLogic));
    });
    thread.IsBackground = true;
    thread.SetApartmentState(ApartmentState.STA);
    thread.Start();
    thread.Join();
}

The MainWindow is one of the two WPF applications I want to use. So a second one will join in the fun o a later stage.

The current setup is working. Everything communicates with each other and stuff, no problems here. I was just wondering if the use of this Thread is correct. When I leave applicationLogic = new ApplicationLogic(); out of the Thread, things are bound to go wrong (for example with creating MessageBox popups, the whole application will freeze here).

Should I keep everything in one thread here? Or is it a better practice to split everything up and/or create a Threadpool? How can I approach that the best way?

Joetjah
  • 6,292
  • 8
  • 55
  • 90
  • 4
    Hint: If you're starting a thread and immediately joining it, it's almost certain you don't need a separate thread. The whole point of threads is to be able to (at least conceptually) do two separate things at once, and you kinda defeat that while you're waiting for the `Join()` to return. – cHao May 15 '13 at 13:41
  • @cHao The applicationLogic is supposed to run indefinately. I guess that means I should keep it all in this thread. I use `Join` because it'll shutdown otherwise somehow... – Joetjah May 15 '13 at 13:45
  • It probably shuts down because background threads don't keep the program alive. (I didn't even realize you could `Join()` them, but eh.) The app will only stick around as long as any *non*-background threads are running. – cHao May 15 '13 at 13:47
  • @cHao Ahhh... So if I don't set them as background threads, I don't need to use `Join`. Then I could also split the stuff up in multiple threads. Correct? I still wonder if both applications can reach out for each other's data... – Joetjah May 15 '13 at 13:50
  • It seems to me that if this setup is mostly working for you, then you could do it all in the main thread. I'm not seeing where you really gain anything from forking it off. But i don't have the whole picture; it does kinda depend on what this function is a part of. – cHao May 15 '13 at 13:53
  • @cHao Me neither, but I wondered by asking this question if there are some situations where splitting them up would be a better practice. Perhaps if you want to restart something? Or if the GUI crashes, you can shut down that thread without letting the other GUI and logic die? – Joetjah May 15 '13 at 13:55
  • Typically if you want to restart something, it's because it's misbehaving. Once a thread's reached that point, unless you built in a provably always-working self destruct button, you can't really trust it to shut down properly. `Interrupt()` only works if the thread's blocked waiting for something in managed code, and `Abort()` can get extremely messy (leaving locks locked, etc). At that point, it's really better to just let the whole app die. If you want that kind of control, you kinda need each subprogram in its own app domain, so you can tear down the whole app domain and everything in it. – cHao May 15 '13 at 14:03
  • Course, if you do that, communication between the subprograms gets more complicated (and generally, slower). – cHao May 15 '13 at 14:06

1 Answers1

0

The applicationLogic is supposed to run indefinitely.

I think you're mixing the need for a globally existent class instance and threads. You don't need a separate thread for this, you just need ApplicationLogic to be a Singleton.

public class ApplicationLogic
{
    private static ApplicationLogic _instance = new ApplicationLogic();
    public static ApplicationLogic Instance { get { return _instance; } }

    private ApplicationLogic() { }
}

Further, by performing an immediate thread.Join();, you're making the Thread a moot point. You don't need this thread, just start up the main form. And if you wanted to load another form, just do it, create a new instance and show it:

var otherForm = new OtherForm();
otherForm.Show();

and so now that we are making ApplicationLogic a Singleton, which is what you were doing with the other thread (kind of), you can just access it like this:

ApplicationLogic.Instance.DoSomething();
Mike Perrenoud
  • 66,820
  • 29
  • 157
  • 232
  • That's how I approached it at first. But when I try to run this, I get the error `The calling thread must be STA, because many UI components require this.` when creating the form by `MainWindow newForm = new MainWindow(applicationLogic);`. That's why I created a Thread around it. – Joetjah May 15 '13 at 14:09
  • And that message is given to me when I'm not using any Thread, but just start up the main form. But I don't understand it very well yet so I might have messed something up. – Joetjah May 15 '13 at 14:10
  • A capital-S Singleton may be overkill here...and it tempts code to say `ApplicationLogic.Instance` rather than using the object it was given. The superapp can keep an ApplicationLogic as a private field, and pass it to the `MainWindow` constructor as needed. – cHao May 15 '13 at 14:10
  • I just created a static ApplicationLogic instance. That should be enough to just pass along as parameter. As far as I know, by passing that instance to different classes, it's a pointer to the same instance and not different instances, correct? – Joetjah May 15 '13 at 14:12
  • 1
    @Joetjah: Yeah. In .net, most object variables (everything but value-type variables) are references. Grab a copy of the reference, and both copies refer to the same object. – cHao May 15 '13 at 14:14
  • @cHao Thought so :) It still leaves me with the previous error though, the only way I got it to work was by encapsulating everything in a thread... – Joetjah May 15 '13 at 14:15
  • I don't have VS handy... is there something in the project properties that lets you set the app's apartment mode? – cHao May 15 '13 at 14:16
  • @Joetjah, add the `[STAThread]` attribute above your method that starts up the application and that will fix the error. Here is a good article on **why it's necessary** http://stackoverflow.com/questions/1361033/what-does-stathread-do. – Mike Perrenoud May 15 '13 at 14:31
  • @MichaelPerrenoud I've placed that above my `StartTheWholeBunch` method. In that method, a new `MainWindow(applicationLogic)` is being created. I still get the error on the constructor of `MainWindow`. Doesn't everything created in the method annotated with `[STAThread]` automatically take over that utility? In that case, it doesn't seem to usefull then? – Joetjah May 15 '13 at 14:41
  • @Joetjah, is `StartTheWholeBunch` your startup method for the application? Check the `Properties` of the `Project`. – Mike Perrenoud May 15 '13 at 14:42
  • @MichaelPerrenoud You were right. It works now but everything now gets shut down after it all started up. I think I'm gonna stick with my old way... – Joetjah May 15 '13 at 14:47
  • @Joetjah, well what code do you have in the main method now? At most it should be the three lines that were inside of the `Thread`. – Mike Perrenoud May 15 '13 at 14:50
  • @MichaelPerrenoud The main method instantiates a class, in which `StartTheWholeBunch` is being called. Actually everything should be in that first class, but that aside, I get the feeling everything created in a method annotated with `[STAThread]` shares that same annotation. Is that so? – Joetjah May 15 '13 at 14:53
  • @Joetjah, did you read the link I provided that gives a good set of answers surrounding `[STAThread]`? It simply means that it starts up the Windows message pump on a single-apartment thread. This is necessary for Windows applications (unless of course you want to start everything up on its own thread like you did). It should be as simple as a method with `[STAThread]` and then application run with a window to receive messages. – Mike Perrenoud May 15 '13 at 14:56