通过ThreadPool跟踪失败工人的强大方法
-
25-09-2019 - |
题
我正在寻找一种良好的跟踪方法(计数),当与ThreadPool排队并使用Waithandle.Waitall()以完成所有线程完成时,工人失败了。
互锁是一种好的技术还是有更强大的策略?
解决方案
好的,这是您可以采用的一种方法。我已经封装了要跟踪的数据 TrackedWorkers
. 。该课程上有一个构造函数,使您能够设置有多少工人工作。然后,使用 LaunchWorkers
需要一个吃的代表 object
并返回a bool
. 。这 object
代表工人的输入和 bool
取决于成功或失败 true
或者 false
分别是回报值。
因此,基本上我们要做的是跟踪工人状态的数组。我们启动工人并根据工人的退货值设置与该工人相对应的状态。工人返回时,我们设置了 AutoResetEvent
和 WaitHandle.WaitAll
全部 AutoResetEvents
要设置。
请注意,有一个嵌套类来跟踪工人应该做的(代表),该工作的输入以及一个 ID
用于设置状态 AutoResetEvent
对应于该线程。
非常仔细地注意,一旦完成工作,我们就没有提及工作代表 func
也不是 input
. 。这很重要,因此我们不会意外阻止收集垃圾的东西。
有一些方法可以获得特定工人的地位,以及成功的工人的所有索引以及失败的工人的所有索引。
最后一个注意事项:我不认为该代码制作准备就绪。这只是我要采用的方法的草图。您需要注意添加测试,异常处理和其他此类细节。
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;
}
}
}
}
示例用法:
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);
}
}
以上程序将启动64个线程。这 i
线程的任务是添加数字 i
和 2 * i
并将结果打印到控制台。但是,我添加了随机的睡眠(少于一秒钟)来模拟忙碌,然后翻转硬币以确定线程的成功或失败。那些成功打印他们任务任务并返回的金额的人 true
. 。那些什么都没打印并返回的人 false
.
我在这里用过
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;
}
}