代码之家  ›  专栏  ›  技术社区  ›  Chris S

一种利用ThreadPool跟踪失败工人的鲁棒方法

  •  2
  • Chris S  · 技术社区  · 16 年前

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

    联锁计数器是一种好技术还是有一种更稳健的策略?

    1 回复  |  直到 16 年前
        1
  •  1
  •   jason    16 年前

    好的,这里有一个方法,你可以采取。我已经将我们要跟踪的数据封装到一个类中 TrackedWorkers . 这个类上有一个构造函数,可以让您设置将有多少工人在工作。然后,使用 LaunchWorkers object 并返回一个 bool . 这个 表示工作进程的输入和 布尔 true 或 false

    AutoResetEvent 和 WaitHandle.WaitAll 尽管如此 AutoResetEvents

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

    请非常小心地注意,一旦工作完成,我们就不会引用工作代表 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 th线程的任务是添加数字 我 2 * i . 不合格者不打印任何内容并返回 假的

    这里我用过

    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;
        }
    }
    
    推荐文章