这是我的问题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并行/异步框架。
答案 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();
}
}