|
using System;
using System.Diagnostics;
using System.Threading;
using System.Windows.Forms;
/*
Classes to make threading and interaction with a UI easier.
The goal is to have a thread proc that can feed state and prgress back to the UI
and respond to an abort request.
There are two threading patterns supported:
1. A long process that happens in steps. The UI reports the steps somehow.
2. A long process without steps. All that the main program
requires is a note of the success or failure of the process.
*/
namespace WinFormThreading
{
// class to pass state between interface and thread proc
public class ThreadProcessState
{
// process states
// ready / working / finished / aborted
public enum state {ready, working, done, cancelled, errored}
state m_state = state.ready;
// progress
int m_totalSteps = 1; // default one step
int m_currentStep = 0; //
object m_userData = null; // user data
Exception m_Exception;
public ThreadProcessState() { }
public int TotalSteps { get {return m_totalSteps;} set {m_totalSteps = value;} }
public int CurrentStep { get {return m_currentStep;} set {m_currentStep = value;} }
public state State { get {return m_state;}}
public bool IsDone { get {return (m_state == state.done);} }
public bool IsCancelled { get { return (m_state == state.cancelled); } }
public bool IsErrored { get { return (m_state == state.errored); } }
public bool IsAborted { get { return (m_state == state.cancelled || m_state == state.errored); } }
public bool IsWorking { get { return (m_state == state.working); } }
public object UserData { get { return m_userData; } set { m_userData = value; } }
public Exception Exception { get { return m_Exception; } set { m_Exception = value; } }
// called as the thread is kicked off
public void onStartWork()
{
m_currentStep = 0;
m_state = state.ready;
}
// interface for setting state
public void cancel() { m_state = state.cancelled; } // called mainly the UI
public void start() { m_state = state.working; } // called by the thread class
public void finish() { m_state = state.done; } // called by the thread class
public void setError(Exception ex)
{
m_Exception = ex;
if (m_state != state.cancelled) // don't change state if already cancelled
m_state = state.errored;
}
}
// declare the callback function signature
// users of the class below pass in a function with this pattern
delegate void ProgressStateDelegate(ThreadProcessState processState);
/*
wraps the nitty-gritty of a background task with feedback
*/
public class WorkerBase
{
// the members below are passed in the constructor by the user
// Usually a form or a winform control that implements "Invoke/BeginInvode"
ContainerControl m_sender = null;
// The delegate method (callback) on the sender to call
Delegate m_senderDelegate = null;
// the object to hold state information
ThreadProcessState m_processState;
// Constructor used by caller using ThreadPool (untested)
public WorkerBase() {}
// Constructor called by calle using ThreadPool OR ThreadStart
// see above for params
public WorkerBase(ContainerControl sender, Delegate senderDelegate, ThreadProcessState state)
{
m_sender = sender;
m_senderDelegate = senderDelegate;
m_processState = state;
}
// called to start the work
public void KickOffThread()
{
Thread t = new Thread(new ThreadStart(this.RunProcess));
t.IsBackground = true; //make them a daemon - prevent thread callback issues
t.Start();
}
// Method for ThreadPool QueueWorkerItem (untested)
public void RunProcess(object obj)
{
Thread.CurrentThread.IsBackground = true; //make them a daemon
object[] objArray = (object[]) obj;
m_sender = (System.Windows.Forms.Form) objArray[0];
m_senderDelegate = (System.Delegate) objArray[1];
m_processState = (ThreadProcessState)objArray[2];
LocalRunProcess();
}
// Method for ThreadStart delegate
public void RunProcess()
{
Thread.CurrentThread.IsBackground = true; //make them a daemon
m_processState.onStartWork();
LocalRunProcess();
}
/// <summary>
/// Local Method for the actual work.
/// </summary>
protected virtual void LocalRunProcess()
{
m_processState.start();
// do nothing implementation that just loops
for (int step = 0; step < m_processState.TotalSteps &&
!m_processState.IsAborted; ++step)
{
m_processState.CurrentStep = step;
DoWorkStep(m_processState);
// state is either working or aborted.
// note: the completing step will not callback
// to avoid sending two callbacks with the done state.
if (step < m_processState.TotalSteps-1 &&
m_processState.State == ThreadProcessState.state.working)
Callback();
}
// send the done call back unless already aborted
if (!m_processState.IsAborted)
{
m_processState.finish();
}
Callback();
}
// Overridable by the inheriting class to specialise the work done
protected virtual void DoWorkStep(ThreadProcessState processState)
{
// default is a small delay - only good for testing progress lines.
Thread.Sleep(200);
}
// invoked during and on completion
protected void Callback()
{
// may have to snapshot thread state to avoid > 1 done message
m_sender.BeginInvoke(m_senderDelegate, new object[] { m_processState });
}
}
}
|
By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.
If a file you wish to view isn't highlighted, and is a text file (not binary), please
let us know and we'll add colourisation support for it.
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.