/// <summary> /// The dispatcher logic /// </summary> private void OnDispatcherStart() { while (isRunning) { ProactorQueueData task = null; bool hasTask = false; // do we have any completion task to finalise? SettleCompletedTasks(); // do we have free threads? int threadIndex = GetNextAvailableThread(); if (threadIndex < 0) { // nope, let's wait until we have one dispatcherSignal.WaitOne(); continue; } lock (taskQueueLock) hasTask = taskQueue.TryDequeue(out task); if (!hasTask) { // we can sleep the thread until a new task comes up // but we can check again after 5 minutes in case the signal // was never called dispatcherSignal.WaitOne(TimeSpan.FromMinutes(5)); continue; } if (task == null) { continue; // sanity check } // we can now dispatch this task over to the thread Log($"Dispatching task ({task.QueuedCode}) to thread {threadIndex}"); workerThreads[threadIndex] = new Thread(WorkerThreadMethod) { Name = $"WORKER-{threadIndex}" }; task.ThreadIndex = threadIndex; // to keep track of the thread serving this task workerThreads[threadIndex].Start(task); } // ensure all threads are done for (int i = 0; i < WorkerCount; i++) { workerThreads[i]?.Join(); } SettleCompletedTasks(); }
/// <summary> /// Enqueues a function to the dispatcher queue /// </summary> public ProactorTask Enqueue(Func <object> resultFunction, Action <object> finishCallback = null) { ProactorQueueData data = new ProactorQueueData(resultFunction, finishCallback); Log($"Queueing a new task ({data.QueuedCode})"); lock (taskQueueLock) taskQueue.Enqueue(data); // we can signal to the dispatcher thread to stop waiting for new tasks // and resume dispatcherSignal.Set(); return(data.Task); }
private void SettleCompletedTasks() { lock (finishedQueueLock) { while (finishedQueue.Count > 0) { ProactorQueueData finishedTask = finishedQueue.Dequeue(); Log($"Finishing task ({finishedTask.QueuedCode})"); // call the callback if not null finishedTask.FinishCallback?.Invoke(finishedTask.Task.Result); // free up the corresponding thread int tid = finishedTask.ThreadIndex; workerThreads[tid].Join(); workerThreads[tid] = null; Log($"Freed thread {tid}"); } } }
private void WorkerThreadMethod(object data) { Log($"Worker thread start"); if (data == null && !(data is ProactorQueueData)) { throw new ArgumentException($"{nameof(data)} must be of {nameof(ProactorQueueData)} type"); } ProactorQueueData task = (ProactorQueueData)data; object result = task.Operation(); task.Task.Finished = true; task.Task.Result = result; // we have something to run on completion, on the dispatcher thread, so we queue it // and signal the dispatcher lock (finishedQueueLock) finishedQueue.Enqueue(task); dispatcherSignal.Set(); }