/// <summary>Initializes a new instance of the ThreadAffinityTaskScheduler class with the specified concurrency level.</summary> /// <param name="numberOfThreads">The number of threads that should be created and used by this scheduler.</param> public ThreadAffinityTaskScheduler(int numberOfThreads, bool staThreads = false, WaitHelperFunc waitHelper = null) { // Validate arguments if (numberOfThreads < 1) { throw new ArgumentOutOfRangeException("numberOfThreads"); } // Initialize the tasks collection _items = new BlockingCollection <IExecuteItem>(); // CTS to cancel task dispatching _terminationCts = new CancellationTokenSource(); // Create the contexts (threads) to be used by this scheduler _threads = Enumerable.Range(0, numberOfThreads).Select(i => new ThreadWithAffinityContext(_terminationCts.Token, staThreads, waitHelper, Take)).ToList(); }
/// <summary> /// Construct an instance of ThreadWithAffinityContext to used by ThreadAffinityTaskScheduler /// </summary> internal ThreadWithAffinityContext(CancellationToken token, bool staThread, WaitHelperFunc waitHelper, TakeFunc takeFunc) { _waitHelper = waitHelper; _items = new BlockingCollection <IExecuteItem>(); _takeFunc = takeFunc; _cts = CancellationTokenSource.CreateLinkedTokenSource(token); if (_takeFunc == null) { _takeFunc = Take; } // this makes our override of SynchronizationContext.Wait get called if (_waitHelper == null) { _waitHelper = WaitHelper; } else { base.SetWaitNotificationRequired(); } // use TCS to return SingleThreadSynchronizationContext to the task scheduler var tcs = new TaskCompletionSource <TaskScheduler>(); _thread = new Thread(() => { // install on the current thread SynchronizationContext.SetSynchronizationContext(this); try { tcs.SetResult(TaskScheduler.FromCurrentSynchronizationContext()); // the sync. context is ready, return it to the task scheduler // the thread's core task dispatching loop, terminate-able with token while (true) { var executeItem = _takeFunc(_items, _cts.Token); // execute a Task queued by ThreadAffinityTaskScheduler.QueueTask // or a callback queued by SingleThreadSynchronizationContext.Post executeItem.Execute(); } ; } catch (OperationCanceledException) { // ignore OperationCanceledException exceptions when terminating if (!_cts.Token.IsCancellationRequested) { throw; } } finally { SynchronizationContext.SetSynchronizationContext(null); } }); // make it an STA thread if message pumping is requested if (staThread) { _thread.SetApartmentState(ApartmentState.STA); } _thread.IsBackground = true; _thread.Start(); this.Scheduler = tcs.Task.Result; }