Task.WaitAll 的线程本地对象
我有少量任务,我想使用 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
类仅用于说明目的)。如何为每个线程创建一个对象,而不是为每个任务创建一个对象,这样很浪费?
编辑:
像 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)
I have a small number of tasks, and I want to use Task.WaitAll
to run them in parallel. This is what I have by now:
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)
The problem is that Random
is not thread-safe (Random
class is chosen for illustration purpose only). How can I create an object for each thread rather than create an object for each Task
, which is wasteful?
EDIT:
Using ThreadStatic
like Brian's and Daniel's suggestions is a good approach, especially with a factory class. However, I prefer ThreadLocal
suggested by Reed and Tomas because it looks simpler. I agree that using a master Random
is a bit over-complex. The following is a mixed solution I keep for future reference:
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)
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
您可以将线程特定的状态放入工厂类中并将其标记为
ThreadStatic
。然后你可以这样做:
You could put your thread-specific state in a factory class and mark it
ThreadStatic
.Then you can do:
您可以使用
ThreadLocal<'T>
类 来存储 Random 实例。这将提供一个线程本地随机值,可以在任务完成后将其释放。You can use the
ThreadLocal<'T>
class to store the Random instance. This will provide a thread-local random value, which can be disposed of upon completion of your tasks.StackOverflow 上最近的 F# 问题 完全实现了您的功能正在寻找。
使用线程本地(线程静态)存储是可行的方法,但您应该小心 - 当所有线程同时启动时,它们也有可能获得相同的初始种子。为了解决这个问题,上面的实现使用了一个受锁保护的 master
Random
,该锁用于为各个线程生成种子。A recent F# question here on StackOverflow implements exactly what you're looking for.
Using thread-local (thread-static) storage is the way to go, but you should be careful - when all threads start at the same time, there is a chance that they'll also get the same initial seed. To solve this problem, the implementation above uses a master
Random
protected by a lock that is used to generate seeds for individual threads.您可以将“rand”声明为 ThreadStatic。或者,TPL 中有一些允许 TLS 的 API;我不知道全部,但请参阅此处< /a>,可能是寻找其他人的起点。 (希望其他人能够发布更多细节或完善这个答案。)
You could declare 'rand' as a ThreadStatic. Alternatively, there are some APIs in the TPL that allow for TLS; I don't know them all, but see e.g. here, may be jumping off point to find others. (Hopefully others will post more details or refine this answer.)
我认为您不需要本地线程。您可以使用当前线程 ID 在每个任务中创建新的 Random 对象。
例如:
我认为这比跟踪线程局部变量更简单、更容易理解。
I don't think you event need Thread Local. You can create the new Random object in each task using the current thread ID.
Ex:
Which I think is much more simple and easy to understand rather than tracking the Thread local variable.