/// <param name="threadCount">The desired number of threads to create. If not specified, the default is based on the number of processors available.</param> /// <param name="createBackgroundThreads">Whether to create background threads for processing work. Non-background threads will block the program from exiting until their work is complete.</param> /// <param name="timeProvider">The time provider used to measure elapsed times during work item processing.</param> /// <param name="comThreadingModel">If you don't already know what this is, you probably don't care about it.</param> /// <param name="name">A name used to identify the threads owned by this group for debugging purposes.</param> /// <param name="enableCoarseTime">If set, a background thread will be created responsible for reading the system clock. This will improve main thread step performance at the cost of extra CPU usage.</param> public ThreadGroup( int?threadCount = null, bool createBackgroundThreads = false, ITimeProvider timeProvider = null, ApartmentState comThreadingModel = ApartmentState.Unknown, string name = null, bool enableCoarseTime = false ) { Name = name; ThreadCount = Math.Min(threadCount.GetValueOrDefault(Environment.ProcessorCount), MaximumThreadCount); CreateBackgroundThreads = createBackgroundThreads; TimeProvider = timeProvider ?? Time.DefaultTimeProvider; COMThreadingModel = comThreadingModel; Threads = new GroupThread[ThreadCount]; for (int i = 0; i < Threads.Length; i++) { Threads[i] = new GroupThread(this, i); } SynchronizationContext = SynchronizationContext.Current; if (enableCoarseTime) { TimeThread = new Thread(TimeThreadProc) { IsBackground = true }; TimeThread.Start(); } }
private static bool ThreadMainStep(WeakReference <GroupThread> weakSelf, ref int queueIndex, out bool moreWorkRemains) { // We hold the strong reference at method scope so we can be sure it doesn't get held too long GroupThread strongSelf = null; moreWorkRemains = false; // If our owner object has been collected, abort if (!weakSelf.TryGetTarget(out strongSelf)) { return(false); } // If our owner object has been disposed, abort if (strongSelf.IsDisposed) { return(false); } int queueCount; IWorkQueue[] queues; lock (strongSelf.Queues) { queueCount = strongSelf.Queues.Count; queues = strongSelf.Queues.GetBuffer(); } strongSelf.Owner.ThreadBeganWorking(); for (int i = 0; i < queueCount; i++) { // We round-robin select a queue from our pool every tick and then step it IWorkQueue queue = null; if (queueIndex < queueCount) { queue = queues[queueIndex]; } queueIndex = (queueIndex + 1) % queueCount; if (queue != null) { bool exhausted; int processedItemCount = queue.Step(out exhausted); // HACK: If we processed at least one item in this queue, but more items remain, // make sure the caller knows not to go to sleep. if (processedItemCount > 0) { moreWorkRemains |= !exhausted; } } } strongSelf.Owner.ThreadBecameIdle(); GC.KeepAlive(strongSelf); return(true); }
internal void RegisterQueuesForNewThread(GroupThread newThread) { lock (QueueList) foreach (var queue in QueueList) { newThread.RegisterQueue(queue); } }
private void SpawnThread(bool force) { // Just in case thread state gets out of sync... if ((Threads.Count >= MaximumThreadCount) && !force) { return; } Interlocked.Exchange(ref LastTimeThreadWasIdle, TimeProvider.Ticks); var thread = new GroupThread(this); Threads.Add(thread); Thread.MemoryBarrier(); HasNoThreads = false; CanMakeNewThreads = Threads.Count < MaximumThreadCount; CurrentThreadCount = Threads.Count; Thread.MemoryBarrier(); }
private static ThreadIdleManager ThreadMainStep( WeakReference <GroupThread> weakSelf, ref bool running ) { // We hold the strong reference at method scope so we can be sure it doesn't get held too long GroupThread strongSelf = null; // If our owner object has been collected or disposed, abort if (!weakSelf.TryGetTarget(out strongSelf) || strongSelf.IsDisposed) { running = false; return(null); } if (strongSelf.IdleManager.BeginRunning() && strongSelf.PerformWork()) { return(strongSelf.IdleManager); } else { return(null); } }