views:

511

answers:

9

I've been working on the same project now since Christmas. I've been asked to take it from a Console Application (which just prints out trace statements), to a full Windows App. Sure, that's fine. The only thing is there are parts of the App that can take several minutes to almost an hour to run. I need to multithread it to show the user status, or errors. But I have no idea where to begin.

I've aready built a little UI in WPF. It's very basic, but I'd like to expand it as I need to. The app works by selecting a source, choosing a destination, and clicking start. I would like a listbox to update as the process goes along. Much in the same way SQL Server Installs, each step has a green check mark by its name as it completes.

How does a newbie start multithreading? What libraries should I check out? Any advice would be greatly appreciated.

p.s. I'm currently reading about this library, http://www.codeplex.com/smartthreadpool

@Martin: Here is how my app is constructed:

  1. Engine: Runs all major components in pre-defined order
  2. Excel: Library I wrote to wrap COM to open/read/close/save Workbooks
  3. Library: Library which understands different types of workbook formats (5 total)
  4. Business Classes: Classes I've written to translate Excel data and prep it for Access
  5. Db Library: A Library I've written which uses ADO.NET to read in Access data
  6. AppSettings: you get the idea
  7. Serialier: Save data in-case of app crash

I use everything from LINQ to ADO.NET to get data, transform it, and then output it.

My main requirement is that I want to update my UI to indicate progress

@Frank: What happens if something in the Background Worker throws an Exception (handled or otherwise)? How does my application recieve notice?

@Eric Lippert: Yes, I'm investigating that right now. Before I complicate things.

Let me know if you need more info. Currently I've running this application from a Unit Test, so I guess callig it a Console Application isn't true. I use Resharper to do this. I'm the only person right now who uses the app, but I'd like a more attractive interface

A: 

The best way for a total newcomer to threading is probably the threadpool. We'll probably need to know a little more about these parts to make more in depth recommendations

EDIT::
Since we now have a little more info, I'm going to stick with my previous answer, it looks like you have a loads of tasks which need doing, the best way to do a load of tasks is to add them to the threadpool and then just keep checking if they're done, if tasks need to be done in a specific order then you can simply add the next one as the previous one finishes. The threadpool really is rather good for this kind of thing and I see no reason not to use it in this case

Martin
+6  A: 

I don't think you specify the version of the CLR you are using, but you might check out the "BackgroundWorker" control. It is a simple way to implemented multiple threads.

The best part, is that it is a part of the CLR 2.0 and up

Update in response to your update: If you want to be able to update the progress in the UI -- for example in a progress bar -- the background worker is perfect. It uses an event that I think is called: ProgressChanged to report the status. It is very elegant. Also, keep in mind that you can have as many instances that you need and can execute all the instances at the same time (if needed).

In response to your question: You could easily setup an example project and test for your question. I did find the following, here (under remarks, 2nd paragraph from the caution):

If the operation raises an exception that your code does not handle, the BackgroundWorker catches the exception and passes it into the RunWorkerCompleted event handler, where it is exposed as the Error property of System.ComponentModel..::.RunWorkerCompletedEventArgs.

Frank V
I think so. It is just a .net class.
Frank V
Thanks, I'll read into it.
Chris
Just note, before jumping into multithreading via background worker - yu HAVE to be aware of thread safety. You HAVE to know that directly accessing/updating data in one thread from another thread is dangerous and unsafe. Other links here introduce you to locking and similar that's needed.
nos
For anyone following this thread, a BGW _does_ work well with WPF. See http://msdn.microsoft.com/en-us/magazine/cc163328.aspx#S4
Henk Holterman
@Henk, I've never used it with WPF, myself... But I believe the article you posted supports it's use.
Frank V
A: 

This page is quite a good summary of threading.

By the sound of it you probably don't need anything very complex - if you just start the task and then want to know when it has finished, you only need a few lines of code to create a new thread and get it to run your task. Then your UI thread can bumble along and check periodically if the task has completed.

Jason Williams
@Jason: It's a bit more than that. I'm processing about 5000+ Workbooks and I'd like to know what workbook is being processed, etc, as the UI sits there.
Chris
If your needs are simple, you can use volatiles to pass values between threads with minimal effort. e.g. if you have a list of workbooks, you could indicate progress using an int that indicates the index of the current workbook. By declaring it as "volatile int myIndex;" you don't need to use locks to safely access it between threads. In the UI thread, you can then add a Forms.Timer that calls you (e.g.) a couple of times a second and you can update the UI by just reading the myIndex and reporting it. If you need more complex data, then put it in a class and use lock around any accesses.
Jason Williams
Note that volatile can only be used with a few types (like bool, int)
Jason Williams
A: 

Concurrent Programming on Windows is THE best book in the existence on the subject. Written by Joe Duffy, famous Microsoft Guru of multithreading. Everything you ever need to know and more, from the way Windows thread scheduler works to .NET Parallels Extensions Library.

zvolkov
How does a newbie start multithreading? I doubt with this book.
iik
+4  A: 

Threading in C# from Joseph Albahari is quite good.

iik
A: 

Remember to create your delegates to update the UI so you don't get cross-threading issues and the UI doesn't appear to freeze/lockup

Also if you need a lot of notes/power points/etc etc

Might I suggest all the lecture notes from my undergrad http://ist.psu.edu/courses/SP04/ist411/lectures.html

PSU_Kardi
A: 

Jason's link is a good article. Things you need to be aware of are that the UI can only be updated by the main UI thread, you will get cross threading exceptions if you try to do it in the worker thread. The BackgroundWorker control can help you there with the events, but you should also know about Control.Invoke (or Control.Begin/EndInvoke). This can be used to execute delegates in the context of the UI thread.

Also you should read up on the gotchas of accessing the same code/variables from different threads, some of these issues can lead to bugs that are intermittent and tricky to track down.

One point to note is that the volatile keyword only guarantees 'freshness' of variable access, for example, it guarantees that each read and write of the variable will be from main memory, and not from a thread or processor cache or other 'feature' of the memory model. It doesnt stop issues like a thread being interrupted by another thread during its read-update-write process (e.g. changing the variables value). This causes errors where the 2 threads have different (or the same) values for the variable, and can lead to things like values being lost, 2 threads having the same value for the variable when they should have different values, etc. You should use a lock/monitor (or other thread sync method, wait handles, interlockedincrement/decrement etc) to prevent these types of problems, which guarantee only one thread can access the variable. (Monitor also has the advantage that it implicitly performs volatile read/write)

And as someone else has noted, you also should try to avoid blocking your UI thread whilst waiting for background threads to complete, otherwise your UI will become unresponsive. You can do this by having your worker threads raise events that your UI subscribes to that indicate progress or completion.

Matt

Matt
A: 

Typemock have a new tool called Racer for helping with Multithreading issues. It’s a bit advanced but you can get help on their forum and in other online forums (one that strangely comes to mind is stackoverflow :-) )

A: 

I'm a newbie to multithreading as well, but I agree with Frank that a background worker is probably your best options. It works through event subscriptions. Here's the basics of how you used it.

  • First Instantiate a new background worker
  • Subscribed methods in your code to the background workers major events:
    • DoWork: This should contain whatever code that takes a long time to process
    • ProgressChanged: This is envoked whenever you call ReportProgress() from inside the method subscribed to DoWork
    • RunWorkerCompleted: Envoked when the DoWork method has completed

When you are ready to run your time consuming process you call the RunAsync() method of the background worker. This starts DoWork method on a separate thread, which can then report it's progress back through the ProgressChanged event. Once it completed RunWorkerComplete will be evoked.

The DoWork event method can also check if the user somehow requested that the process be canceled (CanceLAsync() was called)) by checking the value of the CancelPending property.

Eric Anastas