流程是: User Interface 需要query資料 >> producer 就produce一個delegate(就是c++ 的function pointer) >> 一大堆的query(其實是fucntion), 就由consumer來執行 >> 等所有有關的query都做完, 就invoke main thread(interface的thread) 的有關control refresh一下, 讓它顯示query了回來的資料
e.g. 有一個label要顯示用戶名稱
labelA.Text = queryUserName(userId, labelA); public static string queryUserName(uint id, Control control) { QueryFunction threadStart = delegate { StaticRes.queryUserNameThread(id, control); }; Program.g_QueryThread.Enqueue(threadStart); return "Querying User Name..."; }
StaticRes.queryUserNameThread 就是雖要時間較長, 用來query資料的function
以下是class QueryThread的implementation:
public delegate void QueryFunction(); /// <summary> /// Act as a consumer to the queries produced by the DataGridViewCustomCell /// </summary> public class QueryThread { private struct QueueItem { public Delegate _target; public UInt64 _id; public QueueItem(Delegate target, UInt64 id) { _target = target; _id = id; } } private Object _syncEvents = new Object(); private Queue<QueueItem> _queryQueue = new Queue<QueueItem>(); private EventWaitHandle _waitHandle = new EventWaitHandle(false, EventResetMode.AutoReset); Producer queryProducer; Consumer queryConsumer; public QueryThread() { queryProducer = new Producer(_queryQueue, _syncEvents, _waitHandle); queryConsumer = new Consumer(_queryQueue, _syncEvents); Thread producerThread = new Thread(queryProducer.ThreadRun); Thread consumerThread = new Thread(queryConsumer.ThreadRun); producerThread.IsBackground = true; consumerThread.IsBackground = true; producerThread.Start(); consumerThread.Start(); } public void Enqueue(Delegate item, UInt64 id) { QueueItem queueItem = new QueueItem(item, id); _queryQueue.Enqueue(queueItem); _waitHandle.Set(); } }
按下來就是producer 跟consumer thread
producer主要是fire event 使consumer thread 開始digest那些query
consumer就是執行enqueue了的query function
class Producer { private readonly Queue<QueueItem> _queue; private Object _sync; private EventWaitHandle _handle; public Producer(Queue<QueueItem> q, Object sync, EventWaitHandle waitHandle) { _queue = q; _sync = sync; _handle = waitHandle; } public void ThreadRun() { lock (_sync) { while (true) { //wait until item is enqueued _handle.WaitOne(); //enqueued, tell worker thread if (_queue.Count > 0) { Monitor.Pulse(_sync); Monitor.Wait(_sync,0); } } } } } class Consumer { private readonly Queue<QueueItem> _queue; private Object _sync; public Consumer(Queue<QueueItem> q, Object sync) { _queue = q; _sync = sync; } public void ThreadRun() { lock (_sync) { Delegate query; while (true) { while (_queue.Count == 0) { if (Program.g_CustomDialog.Visible == true) { Program.g_CustomDialog.DialogResult = DialogResult.OK; } Monitor.Wait(_sync); } QueueItem item = _queue.Dequeue(); query = item._target; query.DynamicInvoke(null); } } } }
後來有人在stackoverflow上說 lock後的 while(true) loop 會容易引致deadlock
由於我的threading implementation比較簡單, 就不作改動了, 下次記緊就好
msdn上對c# threading的教學: How to: Synchronize a Producer and a Consumer Thread
但不要跟他的implementation, 看一看這裡
沒有留言:
發佈留言