ThreadPool挫折 - 线程创建超过SetMaxThreads

时间:2012-07-15 02:41:18

标签: c# multithreading threadpool

  • 我进行了I / O密集型操作。
  • 我只希望一次运行5个线程的MAX。
  • 我有8000个任务要排队并完成。
  • 每项任务大约需要15-20秒才能执行。

我看过ThreadPool,但是

        ThreadPool.SetMaxThreads(5, 0);

        List<task> tasks = GetTasks();

        int toProcess = tasks.Count;
        ManualResetEvent resetEvent = new ManualResetEvent(false);

        for (int i = 0; i < tasks.Count; i++)
        {
            ReportGenerator worker = new ReportGenerator(tasks[i].Code, id);
            ThreadPool.QueueUserWorkItem(x =>
            {
                worker.Go();
                if (Interlocked.Decrement(ref toProcess) == 0)
                    resetEvent.Set();
            });
        }

        resetEvent.WaitOne();

我无法弄清楚为什么......我的代码一次执行超过5个线程。我试过setmaxthreads,setminthreads,但是它仍然执行超过5个线程。

发生了什么事?我错过了什么?我应该以另一种方式这样做吗?

由于

5 个答案:

答案 0 :(得分:5)

任务并行库可以帮助您:

List<task> tasks = GetTasks();

Parallel.ForEach(tasks, new ParallelOptions { MaxDegreeOfParallelism = 5 }, 
  task => {ReportGenerator worker = new ReportGenerator(task.Code, id); 
           worker.Go();});

What does MaxDegreeOfParallelism do?

答案 1 :(得分:3)

SetMaxThreads存在一个限制,因为您永远不能将其设置为低于系统上的处理器数量。如果您有8个处理器,将其设置为5与完全不调用该功能相同。

答案 2 :(得分:1)

我认为有一种不同的更好的方法可以解决这个问题。 (请原谅我,如果我不小心Java-ize的一些语法)

这里的主线程有一个在“任务”中要做的事情的列表 - 而不是为每个任务创建线程,当你有这么多的项目时,这实际上是无效的,创建所需数量的线程然后拥有它们根据需要从列表中请求任务。

要做的第一件事是将一个变量添加到此代码来自的类中,以用作指向列表的指针。我们还将添加一个用于所需的最大线程数。

// New variable in your class definition
private int taskStackPointer;
private final static int MAX_THREADS = 5;

创建一个方法,该方法返回列表中的下一个任务并递增堆栈指针。然后为此创建一个新界面:

// Make sure that only one thread has access at a time
[MethodImpl(MethodImplOptions.Synchronized)] 
public task getNextTask()
{
    if( taskStackPointer < tasks.Count )
        return tasks[taskStackPointer++];
    else
        return null;
}

或者,您可以返回任务[taskStackPointer ++]。代码,如果有值,您可以指定为“列表末尾”。但是,这样做可能更容易。

界面:

public interface TaskDispatcher
{
     [MethodImpl(MethodImplOptions.Synchronized)] public task getNextTask();
}

在ReportGenerator类中,更改构造函数以接受调度程序对象:

public ReportGenerator( TaskDispatcher td, int idCode )
{
    ...
}

您还需要更改 ReportGenerator 类,以便处理有一个外部循环,通过调用 td.getNextTask()来请求新任务,当它返回NULL时退出循环。

最后,将线程创建代码更改为:(这只是为了给你一个想法)

taskStackPointer = 0;
for (int i = 0; i < MAX_THREADS; i++) 
{ 
    ReportGenerator worker = new ReportGenerator(this,id);
    worker.Go(); 
} 

通过这种方式,您可以创建所需数量的线程,并使它们全部以最大容量工作。

(我不确定我是否正确使用“[MethodImpl(MethodImplOptions.Synchronized)]”...我比Java更习惯于Java#)

答案 3 :(得分:1)

您的任务列表中将包含8k项,因为您告诉代码将它们放在那里:

List<task> tasks = GetTasks();

也就是说,这个数字与正在使用的线程数无关,因为调试器总是会显示你添加到列表中的项目数。

有多种方法可以确定正在使用的线程数。也许最简单的方法之一是使用调试器进入应用程序并查看线程窗口。你不仅会得到一个计数,而且你会看到每个线程正在做什么(或不做什么)导致我...

关于你的任务正在做什么以及你如何到达一个数字来“限制”线程池,有很多讨论。在大多数用例中,线程池将做正确的事情。

现在回答你的具体问题......

要显式控制并发任务的数量,请考虑一个简单的实现,它涉及将任务集合从List更改为BlockingCollection(将在内部使用ConcurrentQueue)和以下代码来“消耗”工作:

var parallelOptions = new ParallelOptions
{
    MaxDegreeOfParallelism = 5
};

Parallel.ForEach(collection.GetConsumingEnumerable(), options, x =>
{
    // Do work here...
});

将MaxDegreeOfParallelism更改为您确定的任何并发值,适合您正在进行的工作。

您可能会对以下内容感兴趣:

Parallel.ForEach Method

BlockingCollection

克里斯

答案 4 :(得分:0)

它对我有用。这样,您就不能使用数量少于“ minworkerThreads”的工作线程。问题是如果您最多需要五个“ workerthreads”,而“ minworkerThreads”为六个则不起作用。 {

ThreadPool.GetMinThreads(out minworkerThreads,out minportThreads);
ThreadPool.SetMaxThreads(minworkerThreads, minportThreads);

}

MSDN

备注

您不能将辅助线程或I / O完成线程的最大数量设置为小于计算机上处​​理器数量的数量。要确定存在多少个处理器,请检索Environment.ProcessorCount属性的值。另外,您不能将辅助线程或I / O完成线程的最大数目设置为小于对应的最小辅助线程或I / O完成线程的数目。要确定最小线程池大小,请调用GetMinThreads方法。

如果例如通过Internet信息服务(IIS)或SQL Server托管公共语言运行库,则主机可以限制或阻止对线程池大小的更改。

更改线程池中的最大线程数时请小心。虽然您的代码可能会受益,但所做的更改可能会对您使用的代码库产生不利影响。

将线程池大小设置得太大可能会导致性能问题。如果同时执行太多线程,则任务切换开销将成为重要因素。