Теоретически, вы создаете пул потоков с соответствующим образом сконфигурированным (поскольку пулы потоков намного проще в управлении, чем необработанные потоки, когда вы имеете дело с лотами задач), поместите все задачи в пул, а затем подождите, пока все не закончится sh. Сложная часть - «соответствующим образом настроенная». Во-первых, есть количество потоков, которые нужно создать (практическое правило гласит: «Создавайте больше, пока вы не достигнете значительного улучшения производительности в целом» при настройке сценария, и нет, это раздражающе сложно получить полностью автоматически).
Но, во-вторых, что еще более важно, рабочие потоки должны иметь возможность обрабатывать задачи, которые вы им назначаете.
Если ваши задачи связаны с вводом / выводом, многопоточность в Tcl вряд ли сильно поможет (так как Tcl на самом деле очень хорош в асинхронном вводе / выводе), если только вы не делаете один из очень короткого списка вещей, где единственный API является синхронным. Если задачи связаны с памятью, многопоточность - плохая идея, пока вы не приобрели больше памяти! Только для задач, связанных с процессором, потоки действительно могут помочь. Чтобы оптимизировать это, реализация Tcl фокусируется на том, чтобы сохранить количество блокировок действительно небольшим, и делает это, требуя от вас реплицировать большинство вашего состояния между потоками; Вы можете использовать переменную общего доступа, но это не то, что вы получаете по умолчанию. И ваш код, который обеспечивает операции с сетями / выводами / ячейками, должен быть разделен таким образом или ориентирован на потоки (с соответствующими блокировками, если необходимо). Это главное требование; Эффективное распараллеливание сложного фрагмента кода может быть задачей нескольких человек!
Тем не менее, теоретически вы просто создаете локальный пакет (здесь называемый worker
), который обеспечивает реализацию каждого рабочего потока и затем выполните:
package require Thread
set numThreads 8
set pool [tpool::create -maxworkers $numThreads -initcmd {
package require worker
}]
# Launch the tasks in the background
foreach_in_collection item $collection {
# worker::processItem is a command you define in the worker package
set task [tpool::post -nowait $pool [list \
worker::processItem $item]]
set tasks($task) $item
lappend inProgress $task
}
# Wait for them all to complete
while {[llength $inProgress]} {
foreach finished [tpool::wait $pool $inProgress inProgress] {
# Pick up the results from each of the finished tasks
set item $tasks($finished)
set result [tpool::get $pool $finished]
# Not sure what you want to do here
puts "$item mapped to $result"
}
}
Принцип достаточно прост, но создание значимого пакета worker
будет для вас действительно трудным. Вы не хотите заканчивать тем, что все просто стоят в очереди за большим глобальным замком; таким образом, на самом деле может потерять производительность, а не получить ее.