0

需要关于 c# 3.0 中多线程的最佳方法的建议(无并行或任务)

情况是,我有一个包含 500 个项目的队列。在特定时间我只能运行 10 个线程(最大)。下面是我的代码。

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 个线程工作,直到队列计数 > 0。

4

5 回答 5

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它,因为我发现它比显式管理线程更容易。

于 2013-05-06T17:01:32.727 回答
1

您应该使用ThreadPoolwhich 为您管理和优化线程

一旦池中的线程完成其任务,它就会返回到等待线程的队列中,在那里它可以被重用。这种重用使应用程序可以避免为每个任务创建新线程的成本。

线程池通常具有最大线程数。如果所有线程都忙,则将其他任务放入队列中,直到可以在线程可用时对其进行服务。

最好不要干涉,ThreadPool因为它足够聪明地管理和分配线程。但是如果你真的需要这样做,你可以通过使用SetMaxThreads方法设置最大线程数的约束

于 2013-05-06T16:32:06.960 回答
1

因此,您需要处理的只是一个旨在从多线程访问的队列。您使用的是 .NET 4.0,我会说使用 .NET 4.0 BlockingCollection。它不仅会完美运行,而且非常高效。您可以轻松地创建自己的类,该类只是对所有方法Queuelock调用。它也可以工作,但效率不会那么高。(不过,它可能对您的目的足够有效,并且“正确”重写 BlockingCollection 将非常困难。)

一旦你有了那个队列,每个工作人员就可以从那个队列中抓取一个项目,处理它,然后向队列请求另一个。当没有更多线程时,您无需担心结束该线程;它没有更多的工作可以做。

于 2013-05-06T16:33:36.527 回答
0

这是一个简单的生产者-消费者场景。您需要一个像这样的线程安全队列:Creating a blocking Queue<T> in .NET? - 10 个线程可以循环读取和处理作业,直到队列为空。根据您填充队列的方式(在开始处理它之前或在处理它时),您可以在队列变空或通过停止标志发出停止信号时立即结束这些线程。在后一种情况下,您可能需要唤醒线程(例如,使用虚拟作业)。

于 2013-05-06T16:30:58.777 回答
0

与其从外部控制线程,不如让每个线程自己消费数据。

伪代码:

create 10 threads

thread code:
    while elements in queue
    get element from queue
    process element
于 2013-05-06T16:31:57.860 回答