C# 一种利用ThreadPool跟踪失败工人的鲁棒方法
我正在寻找一种很好的方法来跟踪(计算)当使用线程池排队并使用WaitHandle.WaitAll()让所有线程完成时,哪些工作线程失败了C# 一种利用ThreadPool跟踪失败工人的鲁棒方法,c#,.net,threadpool,waithandle,C#,.net,Threadpool,Waithandle,我正在寻找一种很好的方法来跟踪(计算)当使用线程池排队并使用WaitHandle.WaitAll()让所有线程完成时,哪些工作线程失败了 联锁计数器是一种好技术还是有一种更稳健的策略 好的,这里有一个方法,你可以采取。我已经将我们要跟踪的数据封装到一个类中TrackedWorkers。这个类上有一个构造函数,可以让您设置将有多少工人在工作。然后,使用LaunchWorkers启动worker,这需要一个委托,该委托吃掉一个对象,并返回一个bool。对象表示工作人员的输入,bool表示成功或失败,
联锁计数器是一种好技术还是有一种更稳健的策略 好的,这里有一个方法,你可以采取。我已经将我们要跟踪的数据封装到一个类中
TrackedWorkers
。这个类上有一个构造函数,可以让您设置将有多少工人在工作。然后,使用LaunchWorkers
启动worker,这需要一个委托,该委托吃掉一个对象
,并返回一个bool
。对象
表示工作人员的输入,bool
表示成功或失败,这分别取决于true
或false
作为返回值
所以基本上我们做的是,我们有一个数组来跟踪工作状态。我们启动worker并根据worker的返回值设置对应于该worker的状态。当工作人员返回时,我们为所有要设置的AutoResetEvent
设置AutoResetEvent
和WaitHandle.WaitAll
请注意,有一个嵌套类用于跟踪工作人员应该执行的工作(委托)、该工作的输入以及用于设置与该线程对应的状态的AutoResetEvent
请非常小心地注意,一旦工作完成,我们就不会引用工作委托func
,也不会引用输入。这一点很重要,这样我们就不会意外地阻止垃圾收集
有一些方法可以获取特定工作人员的状态,以及成功工作人员的所有索引和失败工作人员的所有索引
最后一个注意事项:我不认为这个代码生产准备好了。这只是我将采取的方法的一个草图。您需要注意添加测试、异常处理和其他此类细节
class TrackedWorkers {
class WorkerState {
public object Input { get; private set; }
public int ID { get; private set; }
public Func<object, bool> Func { get; private set; }
public WorkerState(Func<object, bool> func, object input, int id) {
Func = func;
Input = input;
ID = id;
}
}
AutoResetEvent[] events;
bool[] statuses;
bool _workComplete;
int _number;
public TrackedWorkers(int number) {
if (number <= 0 || number > 64) {
throw new ArgumentOutOfRangeException(
"number",
"number must be positive and at most 64"
);
}
this._number = number;
events = new AutoResetEvent[number];
statuses = new bool[number];
_workComplete = false;
}
void Initialize() {
_workComplete = false;
for (int i = 0; i < _number; i++) {
events[i] = new AutoResetEvent(false);
statuses[i] = true;
}
}
void DoWork(object state) {
WorkerState ws = (WorkerState)state;
statuses[ws.ID] = ws.Func(ws.Input);
events[ws.ID].Set();
}
public void LaunchWorkers(Func<object, bool> func, object[] inputs) {
Initialize();
for (int i = 0; i < _number; i++) {
WorkerState ws = new WorkerState(func, inputs[i], i);
ThreadPool.QueueUserWorkItem(this.DoWork, ws);
}
WaitHandle.WaitAll(events);
_workComplete = true;
}
void ThrowIfWorkIsNotDone() {
if (!_workComplete) {
throw new InvalidOperationException("work not complete");
}
}
public bool GetWorkerStatus(int i) {
ThrowIfWorkIsNotDone();
return statuses[i];
}
public IEnumerable<int> SuccessfulWorkers {
get {
return WorkersWhere(b => b);
}
}
public IEnumerable<int> FailedWorkers {
get {
return WorkersWhere(b => !b);
}
}
IEnumerable<int> WorkersWhere(Predicate<bool> predicate) {
ThrowIfWorkIsNotDone();
for (int i = 0; i < _number; i++) {
if (predicate(statuses[i])) {
yield return i;
}
}
}
}
您如何设想报告其状态的线程?如果他们只是将bool
设置为true
或false
可以吗?我想排队的方法会有一个静态计数器,我没有遵循;计数器似乎只用于告诉您有多少工人成功或失败,而不用于告诉您哪些工人成功或失败。我遗漏了什么?对不起,是的,我只需要一个CPU t,虽然这对logarghh iPhone自动更正很有用,@Jason我需要计数,但也失败了,这对记录+5有好处。我想我可能把这个问题说得很糟糕,否则我会得到更多的答复。一个小改动是委托,而不是lambdaI。我将把它重新表述为lambda很好。事件是一个坏的选择吗?我理解WorkItem的限制,但您在threadpool中放入的项目数量是否有限制?对不起,如果我把这个改成另外两个questions@Chris学生:这个问题有点不清楚;你留下了一些未说明的东西。问问题时尽量精确,当然不要迂腐,这很有帮助。也就是说,我们在对你的问题的评论中澄清了一些事情。你所说的“事件是一个糟糕的选择”是什么意思?作为一种信号机制?至于限制,可以等待的AutoResetEvent
的数量是有限制的。如果您需要更多线程,则必须进行一些工作,将它们拆分到多个AutoResetEvent
集合中。事件用于向UI发回事件发生的信号是,当您将10000个项目排入线程池时会发生什么情况,这一定是队列中某个地方的8000个对象。我可能不会使用这个精确的解决方案,但它已经足够接近了
class Program {
static Random rg = new Random();
static object lockObject = new object();
static void Main(string[] args) {
int count = 64;
Pair[] pairs = new Pair[count];
for(int i = 0; i < count; i++) {
pairs[i] = new Pair(i, 2 * i);
}
TrackedWorkers workers = new TrackedWorkers(count);
workers.LaunchWorkers(SleepAndAdd, pairs.Cast<object>().ToArray());
Console.WriteLine(
"Number successful: {0}",
workers.SuccessfulWorkers.Count()
);
Console.WriteLine(
"Number failed: {0}",
workers.FailedWorkers.Count()
);
}
static bool SleepAndAdd(object o) {
Pair pair = (Pair)o;
int timeout;
double d;
lock (lockObject) {
timeout = rg.Next(1000);
d = rg.NextDouble();
}
Thread.Sleep(timeout);
bool success = d < 0.5;
if (success) {
Console.WriteLine(pair.First + pair.Second);
}
return (success);
}
}
struct Pair {
public int First { get; private set; }
public int Second { get; private set; }
public Pair(int first, int second) : this() {
this.First = first;
this.Second = second;
}
}