2010-02-02 5 views
2

스레드 풀에서 대기중인 작업자와 WaitHandle.WaitAll()을 사용하여 모든 스레드가 완료 될 때 실패한 작업자를 추적 (계산)하는 좋은 방법을 찾고 있습니다.ThreadPool을 사용하여 실패한 작업자를 추적하는 확실한 방법

연동 카운터는 좋은 기술입니까, 아니면 더 강력한 전략입니까?

+0

을 사용하고 어떻게 자신의 상태를보고 스레드를 구상합니까? 단지 'bool'을 'true'또는 'false'로 설정하면 괜찮습니까? – jason

+0

메서드 대기열에 정적 카운터가 있다고 생각했습니다. –

+0

나는 따르지 않습니다. 그것은 카운터가 얼마나 많은 근로자가 성공했는지 실패했는지를 알려주지 만 어떤 근로자가 성공했는지 또는 실패했는지를 알려주지 않는 것처럼 보입니다. 내가 뭘 놓치고 있니? – jason

답변

1

좋아요, 여기 당신이 취할 수있는 접근 방법이 있습니다. 나는 추적하고자하는 데이터를 클래스 TrackedWorkers에 캡슐화했습니다. 이 클래스에는 작업자 수를 설정할 수있는 생성자가 있습니다. 그런 다음 LaunchWorkers을 사용하여 작업을 시작합니다.이 작업에는 object을 먹는 대표가 필요하며 bool을 반환합니다. object은 작업자의 입력을 나타내고 bool은 각각 true 또는 false에 따라 성공 또는 실패를 반환 값으로 나타냅니다.

그래서 기본적으로 우리는 작업자 상태를 추적 할 수있는 배열을 가지고 있습니다. 우리는 작업자를 시작하고 작업자의 반환 값에 따라 해당 작업자에 해당하는 상태를 설정합니다. 작업자가 돌아 오면 AutoResetEvents이 모두 설정되도록 AutoResetEventWaitHandle.WaitAll을 설정합니다.

작업자가 수행해야하는 작업 (위임자), 해당 작업에 대한 입력 및 해당 스레드에 해당하는 AutoResetEvent을 설정하는 데 사용되는 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); 

    } 
} 

위의 프로그램은 예순네 스레드를 시작하는 것입니다. i 스레드는 숫자 i2 * i을 추가하고 결과를 콘솔에 인쇄하는 작업을 수행합니다. 그러나, 나는 바쁨을 시뮬레이트하기 위해 임의의 양의 수면 (1 초 미만)을 추가하고 스레드의 성공 또는 실패를 결정하기 위해 동전을 뒤집습니다. 성공한 사람들은 그들이 할당 된 액수를 출력하고 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; 
    } 
} 
+0

+5 노력. 나는 그 질문에 나쁘게 말한 것 같아요. 아니면 더 많은 대답을 얻을 것 같아요. 하나의 사소한 변경은 람다보다는 대표자입니다 –

+0

람다가 괜찮으므로이를 다시 말하겠습니다. 사건이 나쁜 대안인가? WorkItem 한계를 이해하고 있지만 스레드 풀에 넣는 항목의 수에 관한 한도가 있습니까? 죄송합니다. 다른 질문으로 변 환하면 죄송합니다. –

+0

@Chris S : 음, 약간의 질문이있었습니다. 당신은 몇 가지 것을 불특정로 남겼습니다. 그것은 질문을 할 때 최대한의 정확성을 요구할 때 도움이됩니다. 즉, 귀하의 질문에 대한 의견에서 몇 가지 사항을 정리했습니다. "이벤트가 나쁜 대안입니까?"라는 의미는 무엇입니까? 신호 메커니즘으로? 한계에 관해서는 기다릴 수있는'AutoResetEvent'의 수에 제한이 있습니다. 더 많은 쓰레드가 필요하다면,'AutoResetEvent'의 여러 콜렉션들로 나누기 위해 약간의 작업을해야 할 것입니다. – jason