'.NET How to increase/decrease number of threads by a specific ID?
.NET Framework 4.5.2. I'm trying to update some old code and trying to figure out how to manage the number of running threads for a specific ID. This is the code.
foreach (ThreadGroup thread_group in threadGroups)
{
if (ImportQueues.TryGetValue(thread_group.ThreadGroupID, out BlockingCollection<ImportFrequency> configuration))
{
if (configuration.Count > thread_group.ThreadCount)
{
// increase number of threads running for this group
}
else if (configuration.Count < thread_group.ThreadCount)
{
// decrease number of threads running for this group
}
}
else // Spin up the initial threads
{
ImportQueues.Add(thread_group.ThreadGroupID, new BlockingCollection<ImportFrequency>());
for (int x = 0; x < thread_group.ThreadCount; x++)
{
Thread import_thread = new Thread(new ParameterizedThreadStart(ProcessImportQueue)) { IsBackground = true };
import_thread.Start((ImportQueues[thread_group.ThreadGroupID]));
}
}
}
Essentially, we're running thread_group.ThreadCount number of threads for each group's blocking collection, which will be continually updated. Additionally, thread_group can be updated to change the ThreadCount. If the change increases the number of threads, spin up more to process the blocking collection. If it decreases, wait for the difference in number of threads to finish while the others continue running.
Is this possible with this paradigm or do I need to find a better way to manage threads?
Edit: One solution I tried with this is using CancellationTokens. When I start up a thread, I pass in a model that contains the context as well as a CancellationToken. That model is saved to a global variable. If we need to decrease the number of threads, I go through however many need to be stopped and cancel the token, which stops the infinite loop for that one thread and stops it.
Solution 1:[1]
As I understand the example code, it creates N queues, with M threads processing each queue.
There are a few potential problems with this. I say potential since there might be some special circumstances that motivates such a solution, but Sturgeon's law suggest it might just be a misguided attempt to achieve some unclear goal.
- It is likely that N*M is larger than the amount of hardware threads available, either resulting in having idle threads that consume resources, or having unnecessary context switching if all threads are loaded.
- It is unclear if the queues are in any way different. If they are identical, why not just use a single queue?
- While adding threads is fairly simple, decreasing threads can be a bit complicated. Threads should only be canceled cooperatively, so you need some way to signal that the thread should be cancelled. But if the thread is blocked waiting for items in the queue you might run into situations where you have asked threads to stop, but they have not yet done so, and you are starting threads faster than threads are actually freed, eventually running out of threads. This can probably be solved, but it is unclear if the current solution does this.
- 'Import' sounds like something involving IO, and IO usually does not scale well with the number of threads. In the worst case, parallel IO can even harm performance.
- It is unclear what the actual processing is doing. In my experience, a cpu core can do an incredible amount of work if properly utilized. When things are 'slow' it usually means that the code is doing some kind of unnecessary work. Some junior programmers approach performance problems by attempting multi-threading as the first and last step. When profiling the code and improving efficiency can often give much greater improvements.
For a simple case of a queue that needs to be processed in parallel I would consider using a parallel.Foreach loop over a blocking collection, using the parallelExtensionsExtras for a better partitioner. That should automatically try to balance the number of threads used to available hardware and CPU usage patterns for maximum throughput.
But I would not touch working code without a good understanding what it is trying to do, and why it should be changed to something better. Any preferably only after sufficient automated testing is in place to help avoid introducing new bugs. Changing code just because it is old is a terrible idea.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | JonasH |
