помогите мне рассуждать о потоках F # - PullRequest
4 голосов
/ 29 декабря 2010

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

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 процессоров - но я сбит с толку, что для больших каталогов асинхронная функция просто не отвечает (даже через полчаса) ). Это явно не терпит неудачу, что сбивает меня с толку. Есть ли исчерпанный пул потоков?

Как на самом деле работают эти темы?

Изменить:

Согласно этому документу :

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

: D

Ответы [ 2 ]

6 голосов
/ 29 декабря 2010

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

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

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

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

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

tl; dr: Это работает, как и ожидалось.

0 голосов
/ 29 декабря 2010

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

...