2013-05-15 4 views
1

Прямо сейчас я создаю 3 потока для выполнения некоторой параллельной работы, я настроил его с помощью Threadpooling, так как я хочу, чтобы все потоки выполнялись одновременно, но все потоки завершались прежде чем продолжить. Вот суть кода:Предложение сделать мой threadpool setup безопасным, сухим и масштабируемым

_resetEvents = new ManualResetEvent[3]; 

_resetEvents[0] = new ManualResetEvent(false); 
ThreadPool.QueueUserWorkItem(DoWorkA); 
_resetEvents[1] = new ManualResetEvent(false); 
ThreadPool.QueueUserWorkItem(DoWorkB); 
_resetEvents[2] = new ManualResetEvent(false); 
ThreadPool.QueueUserWorkItem(DoWorkC); 

WaitHandle.WaitAll(_resetEvents); 

Но мои методы все используют один и тот же базовый код, я разбил его на 3-х методов только установить() правую резьбу.

private void DoWorkA(object o) { 
    var workerClass = new WorkerClass(); 
     workerClass.Process(); 
    _resetEvents[0].Set(); 
} 
private void DoWorkB(object o) { 
    var workerClass = new WorkerClass(); 
     workerClass.Process(); 
    _resetEvents[1].Set(); 
} 
private void DoWorkC(object o) { 
    var workerClass = new WorkerClass(); 
     workerClass.Process(); 
    _resetEvents[2].Set(); 
} 

Очевидно, что это не очень DRY, так как я хотел бы иметь 4 или, может быть, 5 нитей, но хотите, чтобы убедиться, что Set() устанавливает правильную _resetEvent, когда он будет завершен.

Любые предложения о том, как я могу безопасно сделать это и сделать его более сухим и масштабируемым?

+2

Лучший подход Parallel.Invoke(), но это Fx 4 и выше. –

ответ

5

Вы можете просто использовать задачи из TPL вместо нитей, и нет никакой необходимости в засаде ручки:

Action execute =() => { 
    var worker = new WorkerClass(); 
    worker.Process(); 
}; 

var task1 = Task.Run(execute); 
var task2 = Task.Run(execute); 
var task3 = Task.Run(execute); 
Task.WaitAll(task1, task2, task3); 

Или, наоборот:

Action execute =() => (new WorkerClass()).Process(); 

Parallel.Invoke(execute, execute, execute); 

Если вам нужно масштабировать это N пунктов, вы можете переключиться на PLINQ или Parallel.For/ForEach и использования:

int numToRun = 42; 
ParallelEnumerable.Range(0, numToRun).ForAll(i => (new WorkerClass()).Process()); 
+0

Спасибо, Рид, но это самый масштабируемый? Мне интересно, хочу ли я 5 потоков одновременно, Task.WaitAll (task1, task2, task3, task4, task5) кажется немасштабируемым –

+0

@MarkKadlec В какой-то момент вы можете переключиться на использование Parallel.For/Parallel.ForEach или даже PLINQ, если вы работаете против множества предметов. В общем, при выполнении большого количества предметов у вас будет коллекция, поэтому PLINQ и Parallel.For/ForEach станут более подходящими. –

+0

Спасибо, Рид, некоторые хорошие идеи здесь, высоко оценили –

1

использование рамки 4+:

Parallel.Invoke(
    () => DoworkA(), 
    () => DoworkB(), 
    () => DoworkC() 
); 

В комплект входит ожидание и исключение-пересылка.

+0

Зачем беспокоиться о трех методах, которые идентичны, в Это дело? (Как только вы вычеркиваете ручку ожидания, три точно такие же ...) –

+0

Да, resetEvents должен идти, а затем все, что практически. –

+0

Спасибо Хенку, но есть ли более суровый способ сделать это? Я думаю, если бы было сказано 5 или 6 потоков, или, что более важно, что-то настраиваемое, был бы безопасный способ сделать это? –

1

Нечто вроде Parallel.ForEach может быть лучше.

В противном случае просто обрабатывать входящий аргумент в качестве индекса в массиве может быть достаточно:

private void DoWork(object index) 
{ 
    var workerClass = new WorkerClass(); 
     workerClass.Process(); 
    _resetEvents[(int)index].Set(); 
} 

ThreadPool.QueueUserWorkItem(DoWork, 0); 
Смежные вопросы