需要建议c#3.0中的多线程最佳方法(无并行或任务)
情况是,我有一个包含500个项目的队列。在特定时间我只能运行10个线程(Max)。以下是我的代码。
While (queue.Count > 0)
{
Thread[] threads = new Thread[no_of_threads];
for (int j = 0; j < no_of_threads; j++)
{
threads[j] = new Thread(StartProcessing);//StartProcessing Dequeue one item each time //for a single thread
threads[j].Start();
}
foreach (Thread objThread in threads)
{
objThread.Join();
}
}
这个方法的问题是,对于一个实例,如果no_of_threads = 10并且其中9个线程完成了处理,并且1个线程仍在工作,我不能退出循环并将工作委托给自由线程直到所有完成10个线程。
我总是需要10个线程才能工作,直到队列数&gt; 0
答案 0 :(得分:4)
使用Semaphore可轻松完成此操作。
我们的想法是创建一个最大计数为N的信号量,其中N是您允许的线程数。循环等待信号量并在获取信号量时对任务进行排队。
Semaphore ThreadsAvailable = new Semaphore(10, 10);
while (Queue.Count > 0)
{
ThreadsAvailable.WaitOne();
// Must dequeue item here, otherwise you could run off the end of the queue
ThreadPool.QueueUserWorkItem(DoStuff, Queue.Dequeue());
}
// Wait for remaining threads to finish
int threadCount = 10;
while (threadCount != 0)
{
ThreadsAvailable.WaitOne();
--threadCount;
}
void DoStuff(object item)
{
ItemType theItem = (ItemType)item;
// process the item
StartProcessing(item);
// And then release the semaphore so another thread can run
ThreadsAvailable.Release();
}
该项目在主循环中出列,因为这可以避免竞争条件,否则处理起来相当混乱。如果你让线程将项目出列,那么线程必须这样做:
lock (queue)
{
if (queue.Count > 0)
item = queue.Dequeue();
else
// There wasn't an item to dequeue
return;
}
否则,当队列中只剩下一个项目时,可能会发生以下事件序列。
main loop checks Queue.Count, which returns 1
main loop calls QueueUserWorkItem
main loop checks Queue.Count again, which returns 1 because the thread hasn't started yet
new thread starts and dequeues an item
main loop tries to dequeue an item and throws an exception because queue.Count == 0
如果你愿意以这种方式处理事情,那么你没事。关键是确保线程在线程退出之前调用信号量上的Release
。您可以使用显式托管的线程或我发布的ThreadPool
方法来实现。我刚刚使用ThreadPool
,因为我发现它比显式管理线程更容易。
答案 1 :(得分:1)
您应该使用管理和优化线程的ThreadPool
一旦池中的线程完成其任务,它就会返回到等待线程的队列,在那里可以重用它。这种重用使应用程序能够避免为每个任务创建新线程的成本。
线程池通常具有最大线程数。如果所有线程都忙,则将其他任务放入队列,直到可以在线程可用时为其提供服务。
最好不要干扰ThreadPool
,因为它足够聪明地管理和分配线程。但是如果你真的需要这样做,你可以使用SetMaxThreads
方法设置最大线程数的约束
答案 2 :(得分:1)
因此,您需要处理的是一个旨在从多线程访问的队列。如果您使用的是.NET 4.0,我会说使用BlockingCollection
。它不仅可以完美地工作,而且非常有效。您可以轻而易举地创建自己的类,只需Queue
lock
调用所有方法。它也会起作用,但效率不高。 (为了你的目的,它可能会有效足够,并且“正确地”重写“BlockingCollection”会非常困难。)
一旦有了这个队列,每个工作人员就可以从该队列中获取一个项目,处理它,然后向队列询问另一个项目。当没有更多时,你不必担心结束那个线程;它没有更多的工作可以做。
答案 3 :(得分:0)
这是一个简单的生产者 - 消费者场景。您需要一个像这样的线程安全队列:Creating a blocking Queue<T> in .NET? - 10个线程可以循环读取和处理作业,直到队列为空。 根据您填充队列的方式(在开始处理或处理队列之前),您可以在队列变空或通过停止标志发出信号停止时立即结束这些线程。在后一种情况下,您可能需要唤醒线程(例如,使用虚拟作业)。
答案 4 :(得分:0)
不要从外部控制线程,而是让每个线程自己使用数据。
伪代码:
create 10 threads
thread code:
while elements in queue
get element from queue
process element