스레드 풀에서 대기중인 작업자와 WaitHandle.WaitAll()을 사용하여 모든 스레드가 완료 될 때 실패한 작업자를 추적 (계산)하는 좋은 방법을 찾고 있습니다.ThreadPool을 사용하여 실패한 작업자를 추적하는 확실한 방법
연동 카운터는 좋은 기술입니까, 아니면 더 강력한 전략입니까?
스레드 풀에서 대기중인 작업자와 WaitHandle.WaitAll()을 사용하여 모든 스레드가 완료 될 때 실패한 작업자를 추적 (계산)하는 좋은 방법을 찾고 있습니다.ThreadPool을 사용하여 실패한 작업자를 추적하는 확실한 방법
연동 카운터는 좋은 기술입니까, 아니면 더 강력한 전략입니까?
좋아요, 여기 당신이 취할 수있는 접근 방법이 있습니다. 나는 추적하고자하는 데이터를 클래스 TrackedWorkers
에 캡슐화했습니다. 이 클래스에는 작업자 수를 설정할 수있는 생성자가 있습니다. 그런 다음 LaunchWorkers
을 사용하여 작업을 시작합니다.이 작업에는 object
을 먹는 대표가 필요하며 bool
을 반환합니다. object
은 작업자의 입력을 나타내고 bool
은 각각 true
또는 false
에 따라 성공 또는 실패를 반환 값으로 나타냅니다.
그래서 기본적으로 우리는 작업자 상태를 추적 할 수있는 배열을 가지고 있습니다. 우리는 작업자를 시작하고 작업자의 반환 값에 따라 해당 작업자에 해당하는 상태를 설정합니다. 작업자가 돌아 오면 AutoResetEvents
이 모두 설정되도록 AutoResetEvent
및 WaitHandle.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
스레드는 숫자 i
및 2 * 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;
}
}
+5 노력. 나는 그 질문에 나쁘게 말한 것 같아요. 아니면 더 많은 대답을 얻을 것 같아요. 하나의 사소한 변경은 람다보다는 대표자입니다 –
람다가 괜찮으므로이를 다시 말하겠습니다. 사건이 나쁜 대안인가? WorkItem 한계를 이해하고 있지만 스레드 풀에 넣는 항목의 수에 관한 한도가 있습니까? 죄송합니다. 다른 질문으로 변 환하면 죄송합니다. –
@Chris S : 음, 약간의 질문이있었습니다. 당신은 몇 가지 것을 불특정로 남겼습니다. 그것은 질문을 할 때 최대한의 정확성을 요구할 때 도움이됩니다. 즉, 귀하의 질문에 대한 의견에서 몇 가지 사항을 정리했습니다. "이벤트가 나쁜 대안입니까?"라는 의미는 무엇입니까? 신호 메커니즘으로? 한계에 관해서는 기다릴 수있는'AutoResetEvent'의 수에 제한이 있습니다. 더 많은 쓰레드가 필요하다면,'AutoResetEvent'의 여러 콜렉션들로 나누기 위해 약간의 작업을해야 할 것입니다. – jason
을 사용하고 어떻게 자신의 상태를보고 스레드를 구상합니까? 단지 'bool'을 'true'또는 'false'로 설정하면 괜찮습니까? – jason
메서드 대기열에 정적 카운터가 있다고 생각했습니다. –
나는 따르지 않습니다. 그것은 카운터가 얼마나 많은 근로자가 성공했는지 실패했는지를 알려주지 만 어떤 근로자가 성공했는지 또는 실패했는지를 알려주지 않는 것처럼 보입니다. 내가 뭘 놓치고 있니? – jason