实现ISynchronizeInvoke
当运行于线程T1的客户端调用一个对象的方法的时候,该对象的方法是在客户端线程上执行的。然而,如果该对象的方法要求始终在一个指定线程T2上执行,该怎么实现呢?这是要求多个线程配合使用的常见情形。例如,.NET Windows窗体和控件要求必须在创建它们的那个线程里处理消息。为了处理这样的情况,.NET提供了ISynchronizeInvoke接口。本文演示了如何实现ISynchronizeInvoke接口,Synchronizer类。Synchronizer类是ISynchronizeInvoke接口的一个通用实现。你可以继承它来进行扩充,也可以直接使用它作为你实现ISynchronizeInvoke的类的成员,代理ISynchronizeInvoke的行为。
public delegate int AddDelegate(int arg1, int arg2);
public class Calculator
{
public int Add(int arg1, int arg2)
{
int threadId = Thread.CurrentThread.GetHashCode();
Console.WriteLine("Calculator thread ID is " + threadId.ToString());
Thread.Sleep(5000);
return arg1 + arg2;
}
}
public class Calculator
{
public int Add(int arg1, int arg2)
{
int threadId = Thread.CurrentThread.GetHashCode();
Console.WriteLine("Calculator thread ID is " + threadId.ToString());
Thread.Sleep(5000);
return arg1 + arg2;
}
}
[Serializable]
class WorkItem : IAsyncResult
{
object[] mArgs;
object mAsyncState;
bool mCompleted;
Delegate mMethod;
ManualResetEvent mEvent;
object mMethodReturnedValue;
public WorkItem(object asyncState, Delegate method, object[] args)
{
mAsyncState = asyncState;
mMethod = method;
mArgs = args;
mEvent = new ManualResetEvent(false);
mCompleted = false;
}
//IAsyncResult properties
object IAsyncResult.AsyncState
{
get { return mAsyncState; }
}
WaitHandle IAsyncResult.AsyncWaitHandle
{
get { return mEvent; }
}
bool IAsyncResult.CompletedSynchronously
{
get { return false; }
}
bool IAsyncResult.IsCompleted
{
get { return mCompleted; }
}
bool Completed
{
get { lock(this) { return mCompleted; } }
set { lock(this) { mCompleted = value;} }
}
public void CallBack()
{
MethodReturnedValue = mMethod.DynamicInvoke(mArgs);
mEvent.Set();
Completed = true;
}
public object MethodReturnedValue
{
get
{
object methodReturnedValue;
lock (this)
{
methodReturnedValue = mMethodReturnedValue;
}
return methodReturnedValue;
}
set
{
lock (this)
{
mMethodReturnedValue = value;
}
}
}
}
class WorkItem : IAsyncResult
{
object[] mArgs;
object mAsyncState;
bool mCompleted;
Delegate mMethod;
ManualResetEvent mEvent;
object mMethodReturnedValue;
public WorkItem(object asyncState, Delegate method, object[] args)
{
mAsyncState = asyncState;
mMethod = method;
mArgs = args;
mEvent = new ManualResetEvent(false);
mCompleted = false;
}
//IAsyncResult properties
object IAsyncResult.AsyncState
{
get { return mAsyncState; }
}
WaitHandle IAsyncResult.AsyncWaitHandle
{
get { return mEvent; }
}
bool IAsyncResult.CompletedSynchronously
{
get { return false; }
}
bool IAsyncResult.IsCompleted
{
get { return mCompleted; }
}
bool Completed
{
get { lock(this) { return mCompleted; } }
set { lock(this) { mCompleted = value;} }
}
public void CallBack()
{
MethodReturnedValue = mMethod.DynamicInvoke(mArgs);
mEvent.Set();
Completed = true;
}
public object MethodReturnedValue
{
get
{
object methodReturnedValue;
lock (this)
{
methodReturnedValue = mMethodReturnedValue;
}
return methodReturnedValue;
}
set
{
lock (this)
{
mMethodReturnedValue = value;
}
}
}
}
class WorkerThread
{
public Thread mThreadObj;
bool mEndLoop;
Mutex mEndLoopMutex;
AutoResetEvent mItemAdded;
Synchronizer mSynchronizer;
Queue mWorkItemQueue;
public WorkerThread(Synchronizer synchronizer)
{
mSynchronizer = synchronizer;
mEndLoop = false;
mThreadObj = null;
mEndLoopMutex = new Mutex();
mItemAdded = new AutoResetEvent(false);
mWorkItemQueue = new Queue();
CreateThread(true);
}
public void QueueWorkItem(WorkItem workItem)
{
lock (mWorkItemQueue.SyncRoot)
{
mWorkItemQueue.Enqueue(workItem);
mItemAdded.Set();
}
}
bool EndLoop
{
get
{
bool result = false;
mEndLoopMutex.WaitOne();
result = mEndLoop;
mEndLoopMutex.ReleaseMutex();
return result;
}
set
{
mEndLoopMutex.WaitOne();
mEndLoop = value;
mEndLoopMutex.ReleaseMutex();
}
}
Thread CreateThread(bool autoStart)
{
if (mThreadObj != null)
{
Debug.Assert(false);
return mThreadObj;
}
ThreadStart threadStart = new ThreadStart(Run);
mThreadObj = new Thread(threadStart);
mThreadObj.Name = "Synchronizer Worker Thread";
if (autoStart)
{
mThreadObj.Start();
}
return mThreadObj;
}
void Start()
{
Debug.Assert(mThreadObj != null);
Debug.Assert(mThreadObj.IsAlive == false);
mThreadObj.Start();
}
bool QueueEmpty
{
get
{
lock (mWorkItemQueue.SyncRoot)
{
if (mWorkItemQueue.Count > 0)
return false;
return true;
}
}
}
WorkItem GetNext()
{
if (QueueEmpty) return null;
lock (mWorkItemQueue.SyncRoot)
{
return (WorkItem)mWorkItemQueue.Dequeue();
}
}
void Run()
{
while (EndLoop == false)
{
while (QueueEmpty == false)
{
if (EndLoop)
return;
WorkItem workItem = GetNext();
workItem.CallBack();
}
mItemAdded.WaitOne();
}
}
public void Kill()
{
Debug.Assert(mThreadObj != null);
if (mThreadObj.IsAlive == false)
{
return;
}
EndLoop = true;
mItemAdded.Set();
// wait for thead to die
mThreadObj.Join();
if (mEndLoopMutex != null)
mEndLoopMutex.Close();
if (mItemAdded != null)
mItemAdded.Close();
}
}
{
public Thread mThreadObj;
bool mEndLoop;
Mutex mEndLoopMutex;
AutoResetEvent mItemAdded;
Synchronizer mSynchronizer;
Queue mWorkItemQueue;
public WorkerThread(Synchronizer synchronizer)
{
mSynchronizer = synchronizer;
mEndLoop = false;
mThreadObj = null;
mEndLoopMutex = new Mutex();
mItemAdded = new AutoResetEvent(false);
mWorkItemQueue = new Queue();
CreateThread(true);
}
public void QueueWorkItem(WorkItem workItem)
{
lock (mWorkItemQueue.SyncRoot)
{
mWorkItemQueue.Enqueue(workItem);
mItemAdded.Set();
}
}
bool EndLoop
{
get
{
bool result = false;
mEndLoopMutex.WaitOne();
result = mEndLoop;
mEndLoopMutex.ReleaseMutex();
return result;
}
set
{
mEndLoopMutex.WaitOne();
mEndLoop = value;
mEndLoopMutex.ReleaseMutex();
}
}
Thread CreateThread(bool autoStart)
{
if (mThreadObj != null)
{
Debug.Assert(false);
return mThreadObj;
}
ThreadStart threadStart = new ThreadStart(Run);
mThreadObj = new Thread(threadStart);
mThreadObj.Name = "Synchronizer Worker Thread";
if (autoStart)
{
mThreadObj.Start();
}
return mThreadObj;
}
void Start()
{
Debug.Assert(mThreadObj != null);
Debug.Assert(mThreadObj.IsAlive == false);
mThreadObj.Start();
}
bool QueueEmpty
{
get
{
lock (mWorkItemQueue.SyncRoot)
{
if (mWorkItemQueue.Count > 0)
return false;
return true;
}
}
}
WorkItem GetNext()
{
if (QueueEmpty) return null;
lock (mWorkItemQueue.SyncRoot)
{
return (WorkItem)mWorkItemQueue.Dequeue();
}
}
void Run()
{
while (EndLoop == false)
{
while (QueueEmpty == false)
{
if (EndLoop)
return;
WorkItem workItem = GetNext();
workItem.CallBack();
}
mItemAdded.WaitOne();
}
}
public void Kill()
{
Debug.Assert(mThreadObj != null);
if (mThreadObj.IsAlive == false)
{
return;
}
EndLoop = true;
mItemAdded.Set();
// wait for thead to die
mThreadObj.Join();
if (mEndLoopMutex != null)
mEndLoopMutex.Close();
if (mItemAdded != null)
mItemAdded.Close();
}
}
public class Synchronizer: ISynchronizeInvoke,IDisposable
{
WorkerThread mWorkerThread;
public bool InvokeRequired
{
get
{
bool res = Object.ReferenceEquals(Thread.CurrentThread, mWorkerThread.mThreadObj);
return res;
}
}
public IAsyncResult BeginInvoke(Delegate method, Object[] args)
{
WorkItem result = new WorkItem(null, method, args);
mWorkerThread.QueueWorkItem(result);
return result;
}
public object EndInvoke(IAsyncResult result)
{
result.AsyncWaitHandle.WaitOne();
WorkItem workItem = (WorkItem)result;
return workItem.MethodReturnedValue;
}
public object Invoke(Delegate method, object[] args)
{
IAsyncResult asyncResult;
asyncResult = BeginInvoke(method,args);
return EndInvoke(asyncResult);
}
public Synchronizer()
{
mWorkerThread = new WorkerThread(this);
}
~Synchronizer()
{
}
public void Dispose()
{
mWorkerThread.Kill();
}
}
{
WorkerThread mWorkerThread;
public bool InvokeRequired
{
get
{
bool res = Object.ReferenceEquals(Thread.CurrentThread, mWorkerThread.mThreadObj);
return res;
}
}
public IAsyncResult BeginInvoke(Delegate method, Object[] args)
{
WorkItem result = new WorkItem(null, method, args);
mWorkerThread.QueueWorkItem(result);
return result;
}
public object EndInvoke(IAsyncResult result)
{
result.AsyncWaitHandle.WaitOne();
WorkItem workItem = (WorkItem)result;
return workItem.MethodReturnedValue;
}
public object Invoke(Delegate method, object[] args)
{
IAsyncResult asyncResult;
asyncResult = BeginInvoke(method,args);
return EndInvoke(asyncResult);
}
public Synchronizer()
{
mWorkerThread = new WorkerThread(this);
}
~Synchronizer()
{
}
public void Dispose()
{
mWorkerThread.Kill();
}
}
public class Tester : IDisposable
{
Synchronizer mSynchronizer;
public Tester()
{
int threadId = Thread.CurrentThread.GetHashCode();
Console.WriteLine("Main Thread Id is: " + threadId.ToString());
mSynchronizer = new Synchronizer();
}
public void TestSynchCall()
{
Calculator calc = new Calculator();
AddDelegate addDelegate = new AddDelegate(calc.Add);
object[] arr = new object[2];
arr[0] = 15;
arr[1] = 38;
object sum = 0;
sum = mSynchronizer.Invoke(addDelegate,arr);
Console.WriteLine("TestSynchCall: result is " + sum.ToString());
}
public void TestAsyncCall()
{
Calculator calc = new Calculator();
AddDelegate addDelegate = new AddDelegate(calc.Add);
object[] arr = new object[2];
arr[0] = 33;
arr[1] = 19;
IAsyncResult result = mSynchronizer.BeginInvoke(addDelegate,arr);
Console.WriteLine("wait for get result from Async Call");
object sum = mSynchronizer.EndInvoke(result);
Console.WriteLine("TestAsyncCall: result is " + sum.ToString());
}
public void Dispose()
{
mSynchronizer.Dispose();
}
}
public class MyClass
{
public static void Main(string[] args)
{
using (Tester tester = new Tester())
{
//tester.TestSynchCall();
tester.TestAsyncCall();
}
Console.ReadLine();
}
}
{
Synchronizer mSynchronizer;
public Tester()
{
int threadId = Thread.CurrentThread.GetHashCode();
Console.WriteLine("Main Thread Id is: " + threadId.ToString());
mSynchronizer = new Synchronizer();
}
public void TestSynchCall()
{
Calculator calc = new Calculator();
AddDelegate addDelegate = new AddDelegate(calc.Add);
object[] arr = new object[2];
arr[0] = 15;
arr[1] = 38;
object sum = 0;
sum = mSynchronizer.Invoke(addDelegate,arr);
Console.WriteLine("TestSynchCall: result is " + sum.ToString());
}
public void TestAsyncCall()
{
Calculator calc = new Calculator();
AddDelegate addDelegate = new AddDelegate(calc.Add);
object[] arr = new object[2];
arr[0] = 33;
arr[1] = 19;
IAsyncResult result = mSynchronizer.BeginInvoke(addDelegate,arr);
Console.WriteLine("wait for get result from Async Call");
object sum = mSynchronizer.EndInvoke(result);
Console.WriteLine("TestAsyncCall: result is " + sum.ToString());
}
public void Dispose()
{
mSynchronizer.Dispose();
}
}
public class MyClass
{
public static void Main(string[] args)
{
using (Tester tester = new Tester())
{
//tester.TestSynchCall();
tester.TestAsyncCall();
}
Console.ReadLine();
}
}