/// <summary> /// Assigns work to a free worker. /// If there is no free workers will block the thread until one becomes availiable or the specified time elapses. /// </summary> /// <param name="data">The data to be used by the worker</param> /// <param name="timeout">The number of milliseconds to wait for a worker to become availiable. /// Use zero to return immediately. Use -1 to wait indefinitely.</param> /// <returns>true if the work was assigned, false otherwise</returns> protected bool AssignWorkWait(TWorkerData data, int timeout) { Worker worker; WorkerTaskObject <TManagerData, TManagerResult, TWorkerData, TWorkerResult> taskObject; if (abortRequested || cancelRequested) { return(false); } // 1. Assure only the Manager Thread access this method if ((this.managerThread == null) || (this.managerThread.ManagedThreadId != Thread.CurrentThread.ManagedThreadId)) { throw new AccessViolationException("Only Manager Thread can access this method"); } // 2. Wait for a worker to be released, or timeout elapses. // This doesn't guarantee that only one thread will cross the synchronization event so // in principle many threads can reach the lock and the Q may be empty while dequeuing. // However, this will never happen because previously we limited the access to this function // to the Manager thread. if (!this.availiableWorker.WaitOne(timeout)) { return(false); } // 3. Acquire Lock. Synchronized access to the idleWorker queue and busyWorkers list. this.workersLock.AcquireWriterLock(-1); Thread.BeginCriticalRegion(); // 4. Get a worker from the idleWorkers queue. // Main thread can not reach this point if the availiableWorker event was not set // which occurs only at initialization or when a woker is released worker = this.idleWorkers.Dequeue(); // 5. Update availiableWorker synchronization event // Notice that availiableWorker event is only set/reset within the workersLock context if (idleWorkers.Count > 0) { this.availiableWorker.Set(); } else { this.availiableWorker.Reset(); } // 6. Reset allWorkersFree synchronization event // Notice that allWorkersFree event is only set/reset within the workersLock context this.allWorkersFree.Reset(); // 7. Add the worker to the busyWorkers list in its corresponding position this.busyWorkers.Insert(worker.WorkerNumber, worker); // 8. Release the lock this.workersLock.ReleaseWriterLock(); Thread.EndCriticalRegion(); // 9. Create the data object to be passed to the worker taskObject = new WorkerTaskObject <TManagerData, TManagerResult, TWorkerData, TWorkerResult>(worker, data); // 10. Start the worker worker.DoWork(taskObject); return(true); }
/// <summary> /// Performs the worker task by calling the Owner.WorkerTask method. /// When it completes, enqueues the worker in the Owner.idleWorkers queue. /// </summary> /// <param name="o">Object that contains the task data to be used during the worker execution</param> private void WorkerThreadTask(object o) { try { WorkerTaskObject <TManagerData, TManagerResult, TWorkerData, TWorkerResult> taskObject = o as WorkerTaskObject <TManagerData, TManagerResult, TWorkerData, TWorkerResult>; if (taskObject == null) { return; } owner.WorkerTask(taskObject); } catch { // Nothing to do here, just ensure the finally block will be executed } finally { owner.FreeWorker(this); } }
/// <summary> /// Configures the thread and begins the execution of the Owner.WorkerTask method. /// </summary> /// <param name="taskObject">Object that contains the task data to be used during the worker execution</param> public void DoWork(WorkerTaskObject <TManagerData, TManagerResult, TWorkerData, TWorkerResult> taskObject) { this.workerThread = new Thread(this.workerThreadTask); this.workerThread.IsBackground = this.runInBackground; this.workerThread.Start(taskObject); }
/// <summary> /// When overriden in a derived class, performs the worker task /// </summary> /// <param name="taskObject">Object that contains the task data with which execute the task</param> protected abstract TWorkerResult WorkerTask(WorkerTaskObject <TManagerData, TManagerResult, TWorkerData, TWorkerResult> taskObject);