I did an article over threads in a command line project. In a command line project, there are less complications than it's GUI alternative. Here, I hope to go into enough detail to put to rest the confusion that threads can introduce to a GUI application. The big hurdle is around the limitation that the only thread to interact with a control is the thread that created the control. In the beginning, I saw this as a silly rule, one not worthy of throwing an exception. The more and more I use threads, the more obvious it becomes that this is less of a suggestion and more of a golden rule. So, the question begs, how do we create a thread to do something that we need a result back from? How do we get any information from a thread that's been started, such as progress, or state of operation? With callbacks, events, and the Invoke method! If we take some of the concepts I discussed in threaded CLR projects, like a payload struct, and use them here, than this will be an easy concept for you.
To start, I'll say that there is a long-named boolean variable we can set to false in the constructor of a form called "CheckForIllegalCrossThreadCalls." When set to false, is does just what you think, it will not check for illegal cross thread calls. To break down what that means, when true it will check to see if a thread tries to access a control it did not create, and when false, it will allow it. In simple applications this may be all you need, but any amount of complexity and it quickly becomes a bad idea. The problem with threads is their unpredictability. Should two threads happen to be editing the same control, things become unstable. From unintended outputs to crashing programs, it's not something you want to test, it's just better to do it right the first time, and every time. This variable, CheckForIllegalCrossThreadCalls, should remain true, by default it is.
For my little example here, the UI will consist of two progress bars and a button. Upon clicking the button, one thread starts per progress bar and they will update the progress bar they're associated with over time. This will leave the GUI thread to do nothing, at least until each thread calls their invokes. In addition to these controls, the form will have two delegates, two events, and a list of all the running threads. The events and delegates are paired. One pair is to update a progress bar, the other pair is for when a thread completes. There is also a struct to act as the payload for the threads, as defined here:
Advice, C#, Development, Source Code