A detailed explanation of the use of STAThread in NET
- 2020-06-07 04:24:05
- OfStack
The primary thread in WindowForm applications is a thread model called "ES2en-ES3en Apartment(STA)". This STA threading model adds message pumps and other mechanisms within threads to reduce the amount of work developers have to write window programs.
When developing the category library, if you want to use a similar STA threading model, you can do so using the categories provided in the following program code.
namespace CLK.Threading
{
public class STAThread
{
// Enum
private enum ThreadState
{
Started,
Stopping,
Stopped,
}
// Fields
private readonly object _syncRoot = new object();
private readonly BlockingQueue<Action> _actionQueue = null;
private Thread _thread = null;
private ManualResetEvent _threadEvent = null;
private ThreadState _threadState = ThreadState.Stopped;
// Constructor
public STAThread()
{
// ActionQueue
_actionQueue = new BlockingQueue<Action>();
// ThreadEvent
_threadEvent = new ManualResetEvent(true);
// ThreadState
_threadState = ThreadState.Stopped;
}
// Methods
public void Start()
{
// Sync
lock (_syncRoot)
{
// ThreadState
if (_threadState != ThreadState.Stopped) throw new InvalidOperationException();
_threadState = ThreadState.Started;
}
// Thread
_thread = new Thread(this.Operate);
_thread.Name = string.Format("Class:{0}, Id:{1}", "STAThread", _thread.ManagedThreadId);
_thread.IsBackground = false;
_thread.Start();
}
public void Stop()
{
// Sync
lock (_syncRoot)
{
// ThreadState
if (_threadState != ThreadState.Started) throw new InvalidOperationException();
_threadState = ThreadState.Stopping;
// ActionQueue
_actionQueue.Release();
}
// Wait
_threadEvent.WaitOne();
}
public void Post(SendOrPostCallback callback, object state)
{
#region Contracts
if (callback == null) throw new ArgumentNullException();
#endregion
// Action
Action action = delegate()
{
try
{
callback(state);
}
catch (Exception ex)
{
Debug.Fail(string.Format("Delegate:{0}, State:{1}, Message:{2}", callback.GetType(), "Exception", ex.Message));
}
};
// Sync
lock (_syncRoot)
{
// ThreadState
if (_threadState != ThreadState.Started) throw new InvalidOperationException();
// ActionQueue
_actionQueue.Enqueue(action);
}
}
public void Send(SendOrPostCallback callback, object state)
{
#region Contracts
if (callback == null) throw new ArgumentNullException();
#endregion
// Action
ManualResetEvent actionEvent = new ManualResetEvent(false);
Action action = delegate()
{
try
{
callback(state);
}
catch (Exception ex)
{
Debug.Fail(string.Format("Delegate:{0}, State:{1}, Message:{2}", callback.GetType(), "Exception", ex.Message));
}
finally
{
actionEvent.Set();
}
};
// Sync
lock (_syncRoot)
{
// ThreadState
if (_threadState != ThreadState.Started) throw new InvalidOperationException();
// ActionQueue
if (Thread.CurrentThread != _thread)
{
_actionQueue.Enqueue(action);
}
}
// Execute
if (Thread.CurrentThread == _thread)
{
action();
}
// Wait
actionEvent.WaitOne();
}
private void Operate()
{
try
{
// Begin
_threadEvent.Reset();
// Operate
while (true)
{
// Action
Action action = _actionQueue.Dequeue();
// Execute
if (action != null)
{
action();
}
// ThreadState
if (action == null)
{
lock (_syncRoot)
{
if (_threadState == ThreadState.Stopping)
{
return;
}
}
}
}
}
finally
{
// End
lock (_syncRoot)
{
_threadState = ThreadState.Stopped;
}
_threadEvent.Set();
}
}
}
}
namespace CLK.Threading
{
public class BlockingQueue<T>
{
// Fields
private readonly object _syncRoot = new object();
private readonly WaitHandle[] _waitHandles = null;
private readonly Queue<T> _itemQueue = null;
private readonly Semaphore _itemQueueSemaphore = null;
private readonly ManualResetEvent _itemQueueReleaseEvent = null;
// Constructors
public BlockingQueue()
{
// Default
_itemQueue = new Queue<T>();
_itemQueueSemaphore = new Semaphore(0, int.MaxValue);
_itemQueueReleaseEvent = new ManualResetEvent(false);
_waitHandles = new WaitHandle[] { _itemQueueSemaphore, _itemQueueReleaseEvent };
}
// Methods
public void Enqueue(T item)
{
lock (_syncRoot)
{
_itemQueue.Enqueue(item);
_itemQueueSemaphore.Release();
}
}
public T Dequeue()
{
WaitHandle.WaitAny(_waitHandles);
lock (_syncRoot)
{
if (_itemQueue.Count > 0)
{
return _itemQueue.Dequeue();
}
}
return default(T);
}
public void Release()
{
lock (_syncRoot)
{
_itemQueueReleaseEvent.Set();
}
}
public void Reset()
{
lock (_syncRoot)
{
_itemQueue.Clear();
_itemQueueSemaphore.Close();
_itemQueueReleaseEvent.Reset();
}
}
}
}