Friday, October 12, 2018

Artistic Multitasking


Multithreading is a wonderful tool but you need to use it very carefully. First don't use it recklessly, as there are specific instances where it is of most benefit (see this post for examples). If you are not in a "cloud" environment that scales-out to add additional CPUs as required, then I have four tips to help you along your way.

1. Tip 1 is to be sure to assign the Name property an each thread you start. This is really the only way to tell which thread caused exceptions and is the only clue when you're debugging of how you got here. Use a name relevant to the instigator method and the serial count of that particular thread.

2. Think in terms of threads working on an instance of an object. This helps keep multiple threads from stepping on each others memory space. Put all the values for input into properties of your new object, create a method in that object that performs the "heavy lifting" and deep calculations, and then execute that method through the thread.Start (placing return values back into a property). As an added tip I like to build an array-list of all the objects I have actively submitted to threads; when the thread terminates I can pop its values out of the array and then null out its instance.

3. If you anticipate that you may ever need the capability to abort the thread you are starting (for example if the user cancels the operation) it is far far better to make provisions for this within the thread, in the method you are executing, rather than using thread.Abort. I usually create a boolean property in my main class called panicStop. All threads check this value within each iteration of a for-loop, and bail out appropriately.

4. Manage the quantity of threads you allow to run simultaneously. Beyond a certain threshold (depending on the number of CPUs on your box and other factors) firing more threads doesn't aid performance much. So manage it.

Multithreading works great as long as you remember to use it for appropriate tasks, and pass each thread a new object to work on. Remember that you are flowing parallel, but in separate "streams", so as to avoid conflating the instance of each object.