Локальные объекты потока для Task.WaitAll - PullRequest
2 голосов
/ 21 октября 2011

У меня небольшое количество задач, и я хочу использовать Task.WaitAll для их параллельного запуска.Вот что у меня сейчас:

type System.Threading.Tasks.Task with
  static member WaitAll(ts) =
    Task.WaitAll [| for t in ts -> t :> Task |]

let processf p fs =
  let rand = Random ()
  let ts = fs |> Array.map (fun f -> Task.Factory.StartNew(fun () -> p(f, rand)))
  Task.WaitAll(ts)
  ts |> Array.map (fun t -> t.Result)

Проблема в том, что Random не является поточно-ориентированным (класс Random выбран только для иллюстрации).Как я могу создать объект для каждого потока, а не создать объект для каждого Task, что расточительно?

РЕДАКТИРОВАТЬ:

Использование ThreadStatic, как у Брайанаи предложения Даниэля - хороший подход, особенно с фабричным классом.Однако я предпочитаю ThreadLocal, предложенный Ридом и Томасом, потому что это выглядит проще.Я согласен, что использование мастера Random немного сложнее.Ниже приведено смешанное решение, которое я оставлю для дальнейшего использования:

let processf p fs =
  use localRand = new ThreadLocal<_>(
                       fun() -> Random(Thread.CurrentThread.ManagedThreadId))
  let ts = fs |> Array.map (fun f -> 
                          Task.Factory.StartNew(fun () -> p(f, localRand.Value)))
  Task.WaitAll(ts)
  ts |> Array.map (fun t -> t.Result)

Ответы [ 5 ]

3 голосов
/ 21 октября 2011

Вы можете поместить свое специфичное для потока состояние в фабричный класс и отметить его ThreadStatic.

type PerThread =

  [<ThreadStatic; DefaultValue>]
  static val mutable private random : Random

  static member Random = 
    match PerThread.random with
    | null -> PerThread.random <- Random(Thread.CurrentThread.ManagedThreadId)
    | _ -> ()
    PerThread.random

Тогда вы можете сделать:

let process p fs =
  let ts = fs |> Array.map (fun f -> 
    Task.Factory.StartNew(fun () -> p(f, PerThread.Random)))
  Task.WaitAll(ts)
  ts |> Array.map (fun (t: Task) -> t.Result)
2 голосов
/ 21 октября 2011

A недавний вопрос F # здесь на StackOverflow реализует именно то, что вы ищете.

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

2 голосов
/ 21 октября 2011

Вы можете использовать ThreadLocal<'T> класс для хранения экземпляра Random.Это обеспечит случайное локальное значение потока, которое может быть удалено после завершения ваших задач.

1 голос
/ 21 октября 2011

Вы можете объявить 'rand' как ThreadStatic . В качестве альтернативы, в TPL есть несколько API, которые допускают TLS; Я не знаю их всех, но вижу, например. здесь , возможно, отскакивает от точки, чтобы найти других. (Надеюсь, другие опубликуют больше деталей или уточнят этот ответ.)

0 голосов
/ 21 октября 2011

Не думаю, что для вашего мероприятия нужна локальная тема.Вы можете создать новый объект Random в каждой задаче, используя текущий идентификатор потока.Пример:

Task.Factory.StartNew(fun () -> p(f, new Random(Thread.CurrentThread.ManagedThreadId))))

Что, я думаю, гораздо проще и понятнее, чем отслеживание локальной переменной Thread.

...