【问题标题】:.NET How to increase/decrease number of threads by a specific ID?.NET 如何通过特定 ID 增加/减少线程数?
【发布时间】:2022-03-10 05:14:02
【问题描述】:

.NET 框架 4.5.2。我正在尝试更新一些旧代码并试图弄清楚如何管理特定 ID 的运行线程数。这是代码。

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]));
        }
    }
}

基本上,我们正在为每个组的阻塞集合运行thread_group.ThreadCount 线程数,这些线程将不断更新。此外,可以更新thread_group 以更改ThreadCount。如果更改增加了线程数,请加快速度以处理阻塞集合。如果它减少,请等待线程数的差异完成,同时其他线程继续运行。

这种范式是否可行,还是我需要找到更好的方法来管理线程?

编辑:我尝试过的一种解决方案是使用 CancellationTokens。当我启动一个线程时,我传入一个包含上下文的模型以及一个 CancellationToken。该模型被保存到一个全局变量中。如果我们需要减少线程的数量,我会经历许多需要停止并取消令牌,这会停止该线程的无限循环并停止它。

【问题讨论】:

  • 一般建议使用线程池而不是手动启动线程。您是否评估过您的解决方案,以确认它确实比更简单的解决方案带来了任何改进?以我的经验,通常有空间更有效地做事,而不是在问题上抛出线程,
  • 您发布的代码是您未描述的问题的当前解决方案。我们可以建议使用更惯用的方式来执行此操作(即线程池),但在不知道此代码解决的实际问题的情况下,我们无法轻易为您提供一个好的具体解决方案。
  • 让我换一种说法。有一个问题X,这个解决方案Y被实施了。您来到这里并清楚地阐明了 Y 是什么以及它的作用。你问我们有没有更好的方法来做 Y。很可能有更好的方法来做 X。如果你能添加 X 的细节并让我们解决这个问题,那就太好了。我怀疑“高层管理人员”会被你的聪明才智所震撼,他们会感激你的改变。
  • @Carspn - 您能否在不提及线程的情况下描述您要处理的内容?我想知道业务问题是什么,而不是您为它设计的解决方案。我想了解您正在尝试做什么,以便我可以决定线程、任务或其他解决方案是否是最好的。
  • @Carspn - 谢谢。我们越来越近了。所以while (true) var x = collection.Take(); 模式有点讨厌。您正在处理一个可能不会做任何工作的线程。线程持有至少 1MB 内存只是为了存在。你想让它们一直工作(即不等待)。如果他们没有工作,他们应该被解雇。我认为这里有很大的空间可以让你的代码在不需要任何线程代码的情况下更快地工作。你能显示完整的代码吗?

标签: c# .net multithreading


【解决方案1】:

根据我对示例代码的理解,它创建了 N 个队列,每个队列有 M 个线程处理。

这有一些潜在问题。我说潜力是因为可能有一些特殊情况会激发这种解决方案,但鲟鱼定律表明这可能只是为了实现一些不明确的目标而被误导的尝试。

  1. N*M 可能大于可用硬件线程的数量,这可能会导致空闲线程消耗资源,或者在加载所有线程时进行不必要的上下文切换。
  2. 不清楚队列是否有任何不同。如果它们相同,为什么不只使用一个队列?
  3. 虽然添加线程相当简单,但减少线程可能有点复杂。线程应该只是canceled cooperatively,所以你需要某种方式来表示线程应该被取消。但是,如果线程被阻塞等待队列中的项目,您可能会遇到您已要求线程停止但它们尚未停止的情况,并且您启动线程的速度比实际释放线程的速度快,最终耗尽线程.这或许可以解决,但目前的解决方案是否能做到这一点尚不清楚。
  4. “导入”听起来像是涉及 IO 的东西,而且 IO 通常不能很好地随线程数扩展。在最坏的情况下,并行 IO 甚至会损害性能。
  5. 目前尚不清楚实际处理的内容。根据我的经验,如果使用得当,CPU 内核可以完成大量工作。当事情“缓慢”时,通常意味着代码正在做某种不必要的工作。一些初级程序员通过尝试多线程作为第一步和最后一步来解决性能问题。在分析代码并提高效率时,通常可以带来更大的改进。

对于需要并行处理的队列的简单情况,我会考虑使用parallel.Foreach loop over a blocking collection,使用parallelExtensionsExtras 以获得更好的分区器。这应该会自动尝试平衡用于可用硬件和 CPU 使用模式的线程数,以实现最大吞吐量。

但如果不充分了解它正在尝试做什么以及为什么应该将其更改为更好的东西,我不会接触工作代码。任何最好只有在足够的自动化测试到位后才能帮助避免引入新的错误。更改代码只是因为它是旧的是一个糟糕的主意。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-02-01
    • 1970-01-01
    • 2021-01-07
    相关资源
    最近更新 更多