<PackageReference Include="NUnit" Version="3.7.0" />

TestWorker

public class TestWorker
A TestWorker pulls work items from a queue and executes them.
using System.Threading; namespace NUnit.Framework.Internal.Execution { public class TestWorker { public delegate void TestWorkerEventHandler (TestWorker worker, WorkItem work); private static Logger log = InternalTrace.GetLogger("TestWorker"); private Thread _workerThread; private int _workItemCount; private bool _running; private WorkItem _currentWorkItem; private object cancelLock = new object(); public WorkItemQueue WorkQueue { get; set; } public string Name { get; set; } public bool IsAlive => _workerThread.IsAlive; public event TestWorkerEventHandler Busy; public event TestWorkerEventHandler Idle; public TestWorker(WorkItemQueue queue, string name) { Guard.ArgumentNotNull(queue, "queue"); WorkQueue = queue; Name = name; } private void TestWorkerThreadProc() { _running = true; try { while (_running) { _currentWorkItem = WorkQueue.Dequeue(); if (_currentWorkItem == null) break; log.Info("{0} executing {1}", _workerThread.Name, _currentWorkItem.Name); _currentWorkItem.TestWorker = this; this.Busy(this, _currentWorkItem); _currentWorkItem.Execute(); this.Idle(this, _currentWorkItem); _workItemCount++; } } finally { log.Info("{0} stopping - {1} WorkItems processed.", Name, _workItemCount); } } public void Start() { log.Info("{0} starting ", Name); _workerThread = new Thread(TestWorkerThreadProc); _workerThread.Name = Name; _workerThread.SetApartmentState(WorkQueue.TargetApartment); _workerThread.Start(); } public void Cancel(bool force) { if (force) _running = false; lock (cancelLock) { if (_workerThread != null && _currentWorkItem != null) { _currentWorkItem.Cancel(force); if (force) _currentWorkItem = null; } } } } }