From d72f19111c893675410ea2941ce7d8671716bd8a Mon Sep 17 00:00:00 2001 From: UbitUmarov Date: Sat, 2 Jan 2021 12:38:25 +0000 Subject: [PATCH] smartthreadpool: place a global throttle on threads creation if pool as less than min number of threads ( 20/s versus .net 1/s in all cases) --- ThirdParty/SmartThreadPool/SmartThreadPool.cs | 63 +++++++++++-------- ThirdParty/SmartThreadPool/WorkItem.cs | 5 +- 2 files changed, 38 insertions(+), 30 deletions(-) diff --git a/ThirdParty/SmartThreadPool/SmartThreadPool.cs b/ThirdParty/SmartThreadPool/SmartThreadPool.cs index 2947d82ac6..74b54b5260 100644 --- a/ThirdParty/SmartThreadPool/SmartThreadPool.cs +++ b/ThirdParty/SmartThreadPool/SmartThreadPool.cs @@ -264,6 +264,8 @@ namespace Amib.Threading /// private bool _isDisposed; + private static long _lastThreadCreateTS = long.MinValue; + /// /// Holds all the WorkItemsGroup instaces that have at least one /// work item int the SmartThreadPool @@ -431,13 +433,19 @@ namespace Amib.Threading private void StartOptimalNumberOfThreads() { - int threadsCount = Math.Max(_workItemsQueue.Count, _stpStartInfo.MinWorkerThreads); - threadsCount = Math.Min(threadsCount, _stpStartInfo.MaxWorkerThreads); - threadsCount -= _workerThreads.Count; - if (threadsCount > 0) + int threadsCount; + lock (_workerThreads.SyncRoot) { - StartThreads(threadsCount); + threadsCount = _workItemsQueue.Count; + if (threadsCount == _stpStartInfo.MinWorkerThreads) + return; + if (threadsCount < _stpStartInfo.MinWorkerThreads) + threadsCount = _stpStartInfo.MinWorkerThreads; + else if (threadsCount > _stpStartInfo.MaxWorkerThreads) + threadsCount = _stpStartInfo.MaxWorkerThreads; + threadsCount -= _workerThreads.Count; } + StartThreads(threadsCount); } private void ValidateSTPStartInfo() @@ -582,26 +590,28 @@ namespace Amib.Threading private void StartThreads(int threadsCount) { if (_isSuspended) - { return; - } lock (_workerThreads.SyncRoot) { // Don't start threads on shut down if (_shutdown) - { return; + + int tmpcount = _workerThreads.Count; + if(tmpcount > _stpStartInfo.MinWorkerThreads) + { + long last = Interlocked.Read(ref _lastThreadCreateTS); + if (DateTime.UtcNow.Ticks - last < 50 * TimeSpan.TicksPerMillisecond) + return; } - for (int i = 0; i < threadsCount; ++i) - { - // Don't create more threads then the upper limit - if (_workerThreads.Count >= _stpStartInfo.MaxWorkerThreads) - { - return; - } + tmpcount = _stpStartInfo.MaxWorkerThreads - tmpcount; + if (threadsCount > tmpcount) + threadsCount = tmpcount; + while(threadsCount > 0) + { // Create a new thread Thread workerThread; if(_stpStartInfo.SuppressFlow) @@ -621,20 +631,21 @@ namespace Amib.Threading ? new Thread(ProcessQueuedItems, _stpStartInfo.MaxStackSize.Value) : new Thread(ProcessQueuedItems); } + // Configure the new thread and start it workerThread.IsBackground = _stpStartInfo.AreThreadsBackground; if (_stpStartInfo.ApartmentState != ApartmentState.Unknown) - { workerThread.SetApartmentState(_stpStartInfo.ApartmentState); - } workerThread.Priority = _stpStartInfo.ThreadPriority; workerThread.Name = string.Format("STP:{0}:{1}", Name, _threadCounter); workerThread.Start(); + Interlocked.Exchange(ref _lastThreadCreateTS, DateTime.UtcNow.Ticks); ++_threadCounter; + --threadsCount; // Add it to the dictionary and update its creation time. _workerThreads[workerThread] = new ThreadEntry(this); @@ -659,6 +670,8 @@ namespace Amib.Threading try { bool bInUseWorkerThreadsWasIncremented = false; + int maxworkers = _stpStartInfo.MaxWorkerThreads; + int minworkers = _stpStartInfo.MinWorkerThreads; // Process until shutdown. while (!_shutdown) @@ -670,11 +683,11 @@ namespace Amib.Threading // The following block handles the when the MaxWorkerThreads has been // incremented by the user at run-time. // Double lock for quit. - if (_workerThreads.Count > _stpStartInfo.MaxWorkerThreads) + if (_workerThreads.Count > maxworkers) { lock (_workerThreads.SyncRoot) { - if (_workerThreads.Count > _stpStartInfo.MaxWorkerThreads) + if (_workerThreads.Count > maxworkers) { // Inform that the thread is quiting and then quit. // This method must be called within this lock or else @@ -694,14 +707,14 @@ namespace Amib.Threading CurrentThreadEntry.IAmAlive(); // On timeout or shut down. - if (null == workItem) + if (workItem == null) { // Double lock for quit. - if (_workerThreads.Count > _stpStartInfo.MinWorkerThreads) + if (_workerThreads.Count > minworkers) { lock (_workerThreads.SyncRoot) { - if (_workerThreads.Count > _stpStartInfo.MinWorkerThreads) + if (_workerThreads.Count > minworkers) { // Inform that the thread is quiting and then quit. // This method must be called within this lock or else @@ -712,11 +725,6 @@ namespace Amib.Threading } } } - } - - // If we didn't quit then skip to the next iteration. - if (null == workItem) - { continue; } @@ -746,6 +754,7 @@ namespace Amib.Threading // will return true, so the post execute can run. if (!workItem.StartingWorkItem()) { + CurrentThreadEntry.CurrentWorkItem = null; continue; } diff --git a/ThirdParty/SmartThreadPool/WorkItem.cs b/ThirdParty/SmartThreadPool/WorkItem.cs index 5b2eec9743..9806000159 100644 --- a/ThirdParty/SmartThreadPool/WorkItem.cs +++ b/ThirdParty/SmartThreadPool/WorkItem.cs @@ -292,14 +292,13 @@ namespace Amib.Threading.Internal { if (IsCanceled) { - bool result = false; if ((_workItemInfo.PostExecuteWorkItemCallback != null) && ((_workItemInfo.CallToPostExecute & CallToPostExecute.WhenWorkItemCanceled) == CallToPostExecute.WhenWorkItemCanceled)) { - result = true; + return true; } - return result; + return false; } Debug.Assert(WorkItemState.InQueue == GetWorkItemState());