CleanCode.Threading ThreadManager
Namespace: CleanCode.Threading
Assembly: CleanCode (in CleanCode.dll) Version: 1.2.3.0 (1.2.03)
The ThreadManager and its ancillary support classes was an early attempt to handle background operations, satisfactory for my limited needs. I urge you to also take a look at the standard BackgroundWorker class that is part of the .NET framework.
Here are the steps needed to hookup background threads with the ThreadManager. In this discussion I will use a PingWorker, a class that pings any number of network addresses and reports the results. Note that while you can supply multiple addresses to a single PingWorker, it checks them serially. I leave it as an exercise to instead use one PingWorker per address and run them concurrently with the ThreadManager.
(1) Create a ThreadManager CompletedNotifier callback method that performs some actions when all background tasks are complete.
private void GuiCallback() { // now that all threads are complete, re-enable your Start button startButton.Enabled = true; // deactivate the polling update timer timer.Stop(); // any other housekeeping needed . . . }
(2) Create a PingWorker PingUpdater callback specific to the PingWorker that updates the GUI for each pinged address. The method shown here is one I created for a compact network scanner. (I have yet to post the source code for this scanner but I would be more than happy to send it to you upon request.) The interface of the network scanner shows a graphical status "light" reflecting the result of the ping. The application further sets a tooltip on it to give more details of the operation.
private void UpdatePingStatus(string ip, string msg, IPStatus ipStatus) { string status = ipStatus.ToString(); string timeNow = DateTime.Now.ToString("HH:mm:ss"); toolTip.SetToolTip(labelMap[ip].StatusLabel, string.Format("{0} @ {1}:{2}{3}", status, timeNow, Environment.NewLine, msg)); labelMap[ip].Image = status.Equals("Success") ? Resources.SuccessIndicator : status.Equals("TimedOut") ? Resources.FailIndicator : Resources.UnknownIndicator; success = success && status.Equals("Success"); }
(3) Create a PingWorker instance, feeding it the callback from step (2). Also provide the list of IP addresses to check. Here, I get them from a dictionary that maps addresses to graphical elements on the GUI mentioned above.
pingWorker = new PingWorker( new PingWorker.PingUpdater(UpdatePingStatus), Timeout); pingWorker.IPAddressList = new List<string>(labelMap.Keys).ToArray();
(4) Create a Timer to poll the PingWorker at regular intervals, which lets you update the GUI on the main thread.
Timer timer = new Timer(); timer.Tick += timer_Tick; timer.Interval = 250;
(5) Create an event handler for the timer that does the GUI updates for the PingWorker.
private void timer_Tick(object sender, EventArgs e) { pingWorker.UpdateResults(); Refresh(); }
(6) Create a ThreadManager instance. There are a variety of constructors that provide flexibility in what it will do, as described earlier. The simplest is just to provide a hook to the callback. Before you add ThreadWorker objects to manage, you must reset the ThreadManager. The argument, if non-zero, specifies an initial value for the ToolStripProgressBar it will maintain for you (if any). Then proceed to add the worker threads (subclasses of ThreadWorker) and finally start it off with the Run method. You also need to start the separate timer that polls the PingWorker to update the GUI.
threadManager = new ThreadManager(GuiCallback); threadManager.Reset(0); threadManager.Add(pingWorker); threadManager.Run(); timer.Start();
Optional items:
- If you provide a ToolStripProgressBar, the progress of all the threads may be visually displayed.
- If you provide a log message list, status messages are added to the list which you may use for diagnostics or display updates.
- If you provide a StructuredTrace, a variety of diagnostic messages will be posted there as well.
Since CleanCode 0.9.07.