.NET 2.0使用ThreadPool处理非常大的列表

时间:2013-02-27 19:36:41

标签: c#

这是我的问题here

通过阅读......我从信号量转移到ThreadPool。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;

namespace ThreadPoolTest
{
    class Data
    {
        public int Pos { get; set; }
        public int Num { get; set; }
    }

    class Program
    {
        static ManualResetEvent[] resetEvents = new ManualResetEvent[20];

        static void Main(string[] args)
        {            

            int s = 0;
            for (int i = 0; i < 100000; i++)
            {                
                resetEvents[s] = new ManualResetEvent(false);
                Data d = new Data();
                d.Pos = s;
                d.Num = i;
                ThreadPool.QueueUserWorkItem(new WaitCallback(Process), (object)d);
                if (s >= 19)
                {
                    WaitHandle.WaitAll(resetEvents);
                    Console.WriteLine("Press Enter to Move forward");
                    Console.ReadLine();
                    s = 0;
                }
                else
                {
                    s = s + 1;
                }
            }
        }

        private static void Process(object o)
        {
            Data d = (Data) o;
            Console.WriteLine(d.Num.ToString());
            Thread.Sleep(10000);
            resetEvents[d.Pos].Set();
        }
    }
}

此代码有效,我可以处理20个集合。但由于WaitAll,我不喜欢这段代码。所以假设我开始一批20个,3个线程需要更长的时间而17个完成。即便如此,由于WaitAll,我将保持17个线程等待。

WaitAny会很好......但是为了有效地使用池,我必须构建如堆栈,列表,队列等大量控制结构似乎相当混乱。

我不喜欢的另一件事是resetEvents类中的整个全局变量。因为必须在Process方法和主循环之间共享此数组。

以上代码有效......但我需要你帮助改进它。

再次......我在.NET 2.0 VS 2008上。我不能使用.NET 4.0并行/异步框架。

2 个答案:

答案 0 :(得分:3)

有几种方法可以做到这一点。根据您上面发布的内容,最简单的可能是:

const int MaxThreads = 4;
const int ItemsToProcess = 10000;
private Semaphore _sem = new Semaphore(MaxThreads, MaxThreads);

void DoTheWork()
{
    int s = 0;
    for (int i = 0; i < ItemsToProcess; ++i)
    {
        _sem.WaitOne();
        Data d = new Data();
        d.Pos = s;
        d.Num = i;
        ThreadPool.QueueUserWorkItem(Process, d);
        ++s;
        if (s >= 19)
            s = 0;
    }

    // All items have been assigned threads.
    // Now, acquire the semaphore "MaxThreads" times.
    // When counter reaches that number, we know all threads are done.
    int semCount = 0;
    while (semCount < MaxThreads)
    {
        _sem.WaitOne();
        ++semCount;
    }
    // All items are processed

    // Clear the semaphore for next time.
    _sem.Release(semCount);
}

void Process(object o)
{
    // do the processing ...

    // release the semaphore
    _sem.Release();
}

我的示例中只使用了四个线程,因为这是我拥有的核心数。使用20个线程没什么意义,因为任何时候只有四个线程可以处理。但如果您愿意,可以随意增加MaxThreads号码。

答案 1 :(得分:2)

所以我很确定这都是.NET 2.0。

我们将开始定义Action,因为我已经习惯使用它了。如果在3.5+中使用此解决方案,请删除该定义。

接下来,我们根据输入创建一个动作队列。

之后我们定义一个回调;这个回调是方法的核心。

它首先抓取队列中的下一个项目(使用锁定,因为队列不是线程安全的)。如果它最终有一个项目要抓住它执行该项目。接下来,它向线程池添加一个新项目,即“自身”。这是一种递归的匿名方法(你不会经常使用这种方法)。这意味着当第一次调用回调时,它将执行一个项目,然后安排将执行另一个项目的任务,该项目将安排执行另一个项目的任务,依此类推。最终队列将耗尽,他们将停止排队更多项目。

我们还希望阻止该方法直到完成所有操作,因此我们通过递增计数器来跟踪这些回调中有多少已完成。当该计数器达到任务限制时,我们会发出事件信号。

最后,我们在线程池中启动N个这些回调。

public delegate void Action();
public static void Execute(IEnumerable<Action> actions, int maxConcurrentItems)
{
    object key = new object();
    Queue<Action> queue = new Queue<Action>(actions);
    int count = 0;
    AutoResetEvent whenDone = new AutoResetEvent(false);

    WaitCallback callback = null;
    callback = delegate
    {
        Action action = null;
        lock (key)
        {
            if (queue.Count > 0)
                action = queue.Dequeue();
        }
        if (action != null)
        {
            action();
            ThreadPool.QueueUserWorkItem(callback);
        }
        else
        {
            if (Interlocked.Increment(ref count) == maxConcurrentItems)
                whenDone.Set();
        }

    };

    for (int i = 0; i < maxConcurrentItems; i++)
    {
        ThreadPool.QueueUserWorkItem(callback);
    }

    whenDone.WaitOne();
}

这是另一个不使用线程池的选项,只使用固定数量的线程:

public static void Execute(IEnumerable<Action> actions, int maxConcurrentItems)
{
    Thread[] threads = new Thread[maxConcurrentItems];
    object key = new object();
    Queue<Action> queue = new Queue<Action>(actions);
    for (int i = 0; i < maxConcurrentItems; i++)
    {
        threads[i] = new Thread(new ThreadStart(delegate
        {
            Action action = null;
            do
            {
                lock (key)
                {
                    if (queue.Count > 0)
                        action = queue.Dequeue();
                    else 
                        action = null;
                }
                if (action != null)
                {
                    action();
                }
            } while (action != null);
        }));
        threads[i].Start();
    }

    for (int i = 0; i < maxConcurrentItems; i++)
    {
        threads[i].Join();
    }
}