我正在寻找一种良好的跟踪方法(计数),当与ThreadPool排队并使用Waithandle.Waitall()以完成所有线程完成时,工人失败了。

互锁是一种好的技术还是有更强大的策略?

有帮助吗?

解决方案

好的,这是您可以采用的一种方法。我已经封装了要跟踪的数据 TrackedWorkers. 。该课程上有一个构造函数,使您能够设置有多少工人工作。然后,使用 LaunchWorkers 需要一个吃的代表 object 并返回a bool. 。这 object 代表工人的输入和 bool 取决于成功或失败 true 或者 false 分别是回报值。

因此,基本上我们要做的是跟踪工人状态的数组。我们启动工人并根据工人的退货值设置与该工人相对应的状态。工人返回时,我们设置了 AutoResetEventWaitHandle.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线程的任务是添加数字 i2 * 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;
    }
}
许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top