2

我正在寻找一种很好的方法来跟踪(计数)哪些工作人员在与线程池排队并使用 WaitHandle.WaitAll() 完成所有线程时失败。

互锁反击是一种好技术还是有更稳健的策略?

4

1 回答 1

1

好的,这是您可以采取的一种方法。我已将我们要跟踪的数据封装到一个类TrackedWorkers中。此类上有一个构造函数,可让您设置将要工作的工人数量。然后,使用LaunchWorkers需要一个吃掉一个object并返回一个的委托来启动工作人员bool。代表工作人员的object输入,bool代表成功或失败,分别取决于返回值truefalse返回值。

所以基本上我们所做的我们有一个数组来跟踪工作状态。我们启动工作人员并根据工作人员的返回值设置与该工作人员对应的状态。当工人返回时,我们设置了一个AutoResetEventand来设置WaitHandle.WaitAll所有的AutoResetEvents

请注意,有一个嵌套类来跟踪工作人员应该执行的工作(委托)、该工作的输入以及ID用于设置AutoResetEvent与该线程对应的状态。

请注意,一旦工作完成,我们不会持有对工作委托funcinput. 这很重要,这样我们就不会意外地阻止东西被垃圾收集。

有一些方法可以获取特定工人的状态,以及成功工人的所有索引和失败工人的所有索引。

最后一点:我不认为这个代码生产准备好了。这只是我将采取的方法的草图。您需要注意添加测试、异常处理和其他此类细节。

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;
    }
}
于 2010-02-02T16:44:53.750 回答