2010-12-29 1 views
4

В лентяйничать вокруг с некоторыми F # (через MonoDevelop), я написал подпрограмму, которая содержит список файлов в директории с одним потоком:помочь мне причину относительно F # резьба

let rec loop (path:string) = 
    Array.append 
    (
     path |> Directory.GetFiles 
    ) 
    (
     path 
     |> Directory.GetDirectories 
     |> Array.map loop 
     |> Array.concat 
    ) 

А потом асинхронная версия этого:

let rec loopPar (path:string) = 
    Array.append 
    ( 
     path |> Directory.GetFiles 
    ) 
    ( 
     let paths = path |> Directory.GetDirectories 
     if paths <> [||] then 
      [| for p in paths -> async { return (loopPar p) } |] 
      |> Async.Parallel 
      |> Async.RunSynchronously 
      |> Array.concat 
     else 
      [||] 
    ) 

В небольших каталогах асинхронная версия работает нормально. В больших каталогах (например, много тысяч каталогов и файлов) асинхронная версия, похоже, зависает. Что мне не хватает?

Я знаю, что создание тысяч потоков никогда не будет самым эффективным решением - у меня всего 8 процессоров, но я сбив с толку, что для больших каталогов асинхронная функция просто не отвечает (даже после половины час). Однако это не заметно, но это меня озадачивает. Есть ли пул потоков, который исчерпан?

Как работают эти потоки?

Edit:

По this document:

Mono> = 2.8.x имеет новый ThreadPool, который намного сложнее тупиковой. Если у вас есть тупиковая ситуация с пустым потоком, вероятность того, что ваша программа пытается зайти в тупик.

: D

+0

звучит как тупик ..... –

+2

WRT в тупик, это очень вероятно. Рассмотрим случай, когда нужно закончить каталог папки B, вам нужно добавить потоки X в пул потоков. Однако это блокируется до тех пор, пока предыдущие потоки не закончатся; за исключением того, что они блокируются необходимостью создавать больше потоков в пуле потоков ... –

+1

Для управляемых стеков из зависающей программы $ PID, «kill -QUIT $ PID» и проверки вывода консоли программы. Для собственных стеков: «gdb attach $ PID», затем «t a a bt». –

ответ

6

Да, скорее всего, вы подавляющий пул потоков Mono, который шлифовальная производительность системы к остановке.

Если вы помните одну вещь из этого, то это темы стоят дорого. Каждый поток нуждается в собственном стеке (мегабайт в размере) и фрагменте процессорного времени (требуется переключение контекста). Из-за этого редко бывает хорошей идеей развернуть свой собственный поток для непродолжительных задач. Вот почему .NET имеет ThreadPool.

ThreadPool - это существующая коллекция потоков для коротких задач, и это то, что пользователи F # используют для своих рабочих процессов Async. Всякий раз, когда вы выполняете операцию F # Async, он просто делегирует действие пулу потоков.

Проблема заключается в том, что происходит, когда вы запускаете тысячи асинхронных действий в F # одновременно? Наивная реализация просто породила столько потоков, сколько необходимо. Однако, если вам нужно 1000 потоков, это означает, что вам нужно 1000 x 4 МБ пространства стека. Даже если у вас достаточно памяти для всех стеков, ваш процессор будет постоянно переключаться между различными потоками. (И пейджинг локальных стеков в и из памяти.)

IIRC, реализация Windows .NET была достаточно умна, чтобы не создавать тонну потоков и просто ставить очередь на работу до тех пор, пока не появятся некоторые запасные потоки для выполнения действий , Другими словами, он будет продолжать добавлять потоки, пока не будет фиксированное число, и просто используйте их. Однако я не знаю, как реализован пул потоков Mono.

tl; dr: Работает должным образом.

+0

Я думаю, что вы правы: здесь с Mono fsi.exe запускает кучу потоков, но поскольку они в основном ничего не делают, система вообще не подвержена стрессу ... –

0

Крис, вероятно, прав. Другой угол, который следует учитывать, заключается в том, что файловые системы не являются фиксированными вещами - эти каталоги с тысячами файлов изменяются, когда вы пытаетесь обработать список? Если это так, это может привести к расовому состоянию где-то.

+0

Это несистемные каталоги с этим ничего не происходит, но это будет проблемой для реального приложения. Я бы предположил, что это одна из причин, по которой методы System.IO.Directory возвращают массивы вместо списков: файловая система изменена и не контролируется средой выполнения. –

Смежные вопросы