If you have ever tried to write a multi-threaded application, you may have found that object-oriented is a difficult goal to achieve. This is largely because you usually have to use static methods and such to spawn your threads. I wasn't too surprised when I discovered this with C/C++ and pthreads on Unix systems, but I was absolutely shocked when I tried to do this in C# using the System.Threading namespace. However, inspiration struck when I was working on an application using the ptlib library in Linux. They have this wonderful class called PThread which you can inherit from: overriding the virtual Main method and allowing you to treat each thread like an individual program in memory making it much easier to organize as a component in your overall architecture. So I thought, hmmm...., there has to be something like this in the .NET environment, but as I searched, I found nothing. So, I decided why not write one?
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
namespace LionFront
{
///
/// This class is an attempt to handle a thread in a completely object oriented manner.
/// The attempt is to make this class similar in concept and funtion to the PTLib PThread class.
/// To use this class, create a class which inherits from this class. Override the Main() method to
/// call the code you want invoked. This will allow you to create mini-programs from within your main thread
/// and to treat them that way
///
public abstract class ThreadClass
{
private Thread thread;
private static int count;
protected bool continue = false;
///
/// Constructor. Initializes a thread to use your class.
///
///
/// The priority for the OS to place on this thread.
///
///
/// Whether or not to start the thread upon construction.
///
///
/// Then name of the class using this one. This will be part of the thread name attribute.
///
public ThreadClass(ThreadPriority priority, bool start, string className)
{
thread = new Thread(new ThreadStart(Main));
thread.Priority = priority;
count++;
thread.Name = className + count.ToString();
if (start)
thread.Start();
}/// Override this method to call the code from your class. protected abstract void Main();
/// This will be invoked by the thread upon startup.
///
public ThreadState State
{
get { return thread.ThreadState; }
}
public string Name
{
get { return thread.Name; }
}
public int ThreadID
{
get { return thread.ManagedThreadId; }
}
public virtual void Start()
{
continue = true;
thread.Start();
}
public virtual void Pause()
{
thread.Suspend();
}
public virtual void Resume()
{
thread.Resume();
}
public virtual void End()
{
continue = false
thread.Abort();
}
}
}
Voila! You have a reusable Threading Class. This is a very simple class. First, we encapsulate a System.Threading.Thread:
private Thread thread;
Next, we have an abstract (the same as virtual void method() = 0; in your c++ header) method that all subclasses must implement.
protected abstract void Main();
Finally, in the constructor, we just tell our thread to use the Main method.
thread = new Thread(new ThreadStart(Main));
Now, how do we use this?
Here is a sample. The following class listens to a socket for input from a remote server and processes the information. If it receives the signal, it will fire the event back to the main program via a delegate.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace LionFront
{
public enum DigitalInput : ushort
{
input1 = 0,
input2,
input3
}
public delegate void DigitalInputCallback(string ipAddress, DigitalInput input);
public class DigitalIO : ThreadClass
{
private static DigitalInputCallback inputCallback;
private static DigitalIO instance;
private static bool initialized;
private static ListipAddressList;
public DigitalIO(DigitalInputCallback callback)
: base(System.Threading.ThreadPriority.Highest, false, "DigitalIO")
{
inputCallback = callback;
if (!initialized)
{
ipAddressList = new List();
initialized = true;
instance = this;
instance.Start();
}
}
~DigitalIO()
{
this.End();
ipAddressList.Clear();
}
protected override void Main()
{
SendReceivePackets inputSignal = new SendReceivePackets(SendReceivePackets.GetIPAddress(), 35100);
inputSignal.BeginListen();
byte[] data = new byte[1000];
while (inputSignal.ReceiveData(ref data))
{
if (continue)
{
int startPos = 0;
int endValue = SendReceivePackets.ParseStream(startPos, data) - startPos;
string ipAddress = Encoding.UTF8.GetString(data, startPos, endValue);
startPos = endValue + startPos + 1;
endValue = SendReceivePackets.ParseStream(startPos, data) - startPos;
ushort input = Convert.ToUInt16(Encoding.UTF8.GetString(data, startPos, endValue));
inputCallback(ipAddress, (DigitalInput)input);
}
}
}
}
Here, we just wrote a normal class (in this case, our own signaling protocol), except for we give it its own Main loop by overriding the Main method. This allows us to write virtually any component that we need to run on its own thread. All we had to do was override the Main Method. Then, to use this component, you would just initialize this class in your application at whatever scope you want it to run on. However, make sure you end the thread when it goes out of scope or it will keep going; thankfully, C# still has destructors. This allows you to have as many different services running inside your application as you would like, and each will run on its own thread.
That is all for now. Happy Coding!
No comments:
Post a Comment