我有少量任务,我想使用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
创建一个对象,这是浪费?
修改
像Brian和Daniel的建议一样使用ThreadStatic
是一种很好的方法,特别是对于工厂类。但是,我更喜欢Reed和Tomas建议的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)
答案 0 :(得分:3)
您可以将特定于线程的状态放在工厂类中,并将其标记为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)
答案 1 :(得分:2)
您可以使用ThreadLocal<'T>
class存储Random实例。这将提供线程局部随机值,可在完成任务后进行处理。
答案 2 :(得分:2)
recent F# question here on StackOverflow实现了您正在寻找的内容。
使用线程局部(线程静态)存储是可行的方法,但是您应该小心 - 当所有线程同时启动时,它们也有可能获得相同的初始种子。为了解决这个问题,上面的实现使用了一个由锁定保护的 master Random
,用于为各个线程生成种子。
答案 3 :(得分:1)
您可以将'rand'声明为ThreadStatic。或者,TPL中有一些允许TLS的API;我不知道所有这些,但请参阅例如here,可能会跳出点找其他人。 (希望其他人会发布更多细节或改进这个答案。)
答案 4 :(得分:0)
我认为您的活动不需要Thread Local。您可以使用当前线程ID在每个任务中创建新的Random对象。 例如:
Task.Factory.StartNew(fun () -> p(f, new Random(Thread.CurrentThread.ManagedThreadId))))
我认为这更简单易懂,而不是跟踪Thread局部变量。