我看过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个线程。
发生了什么事?我错过了什么?我应该以另一种方式这样做吗?
由于
答案 0 :(得分:5)
任务并行库可以帮助您:
List<task> tasks = GetTasks();
Parallel.ForEach(tasks, new ParallelOptions { MaxDegreeOfParallelism = 5 },
task => {ReportGenerator worker = new ReportGenerator(task.Code, id);
worker.Go();});
答案 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更改为您确定的任何并发值,适合您正在进行的工作。
您可能会对以下内容感兴趣:
克里斯
答案 4 :(得分:0)
它对我有用。这样,您就不能使用数量少于“ minworkerThreads”的工作线程。问题是如果您最多需要五个“ workerthreads”,而“ minworkerThreads”为六个则不起作用。 {
ThreadPool.GetMinThreads(out minworkerThreads,out minportThreads);
ThreadPool.SetMaxThreads(minworkerThreads, minportThreads);
}
您不能将辅助线程或I / O完成线程的最大数量设置为小于计算机上处理器数量的数量。要确定存在多少个处理器,请检索Environment.ProcessorCount属性的值。另外,您不能将辅助线程或I / O完成线程的最大数目设置为小于对应的最小辅助线程或I / O完成线程的数目。要确定最小线程池大小,请调用GetMinThreads方法。
如果例如通过Internet信息服务(IIS)或SQL Server托管公共语言运行库,则主机可以限制或阻止对线程池大小的更改。
更改线程池中的最大线程数时请小心。虽然您的代码可能会受益,但所做的更改可能会对您使用的代码库产生不利影响。
将线程池大小设置得太大可能会导致性能问题。如果同时执行太多线程,则任务切换开销将成为重要因素。