Asynchronous 分配工作负载
在我的应用程序中,有许多对象可以执行一些持久的计算,我们称它们为客户机。此外,我还有许多对象,其中包含要计算的任务的描述。大概是这样的:Asynchronous 分配工作负载,asynchronous,f#,Asynchronous,F#,在我的应用程序中,有许多对象可以执行一些持久的计算,我们称它们为客户机。此外,我还有许多对象,其中包含要计算的任务的描述。大概是这样的: let clients = [1..4] let tasks = [1..20] let calculate c t = printf "Starting task %d with client %d\n" t c Thread.Sleep(3000) printf "Finished task %d with client %d\n
let clients = [1..4]
let tasks = [1..20]
let calculate c t =
printf "Starting task %d with client %d\n" t c
Thread.Sleep(3000)
printf "Finished task %d with client %d\n" t c
对于一个客户端,我一次只能启动一个任务。
我想创建一个函数/类,它将任务分配给客户端并执行计算。我在C#中使用一个客户机队列完成了这项工作,因此,一旦将新任务分配给客户机,该客户机将从队列中删除,当计算完成时,客户机将被释放并再次放入队列中。现在我感兴趣的是以一种功能性的方式实现它。我曾经尝试过异步工作流,但是我想不出一个合适的方法来实现它
这是一个类似F#的代码,我试图让它工作,但无法:
let rec distribute clients tasks calculate tasks_to_wait =
match clients, tasks with
| _ , [] -> () // No tasks - we're done!
| [], th::tt -> // No free clients, but there are still tasks to calculate.
let released_client = ?wait_for_one? tasks_to_wait
distribute [released_client] tasks calculate ?tasks_to_wait?
| ch::ct, th::tt -> // There are free clients.
let task_to_wait() =
do calculate ch th
ch
distribute ct tt calculate (task_to_wait::tasks_to_wait)
我该怎么做?是否有一种功能设计模式来解决此任务?有多种方法来完成此任务。使用F#的.NET 4.0中的一些并发集合(如队列)是非常好的,因为如果集合已经实现了您需要的功能,这通常是最容易做到的 这个问题需要并发访问某些资源,因此不能用纯功能的方式解决,但F#提供了代理,这为您提供了一种很好的解决问题的替代方法 下面的代码段实现了一个代理,用于调度工作项。它使用自己的邮箱来保存可用的客户端(这为您提供了一种等待下一个可用客户端的好方法)。创建代理后,您可以只发送所有初始客户端。当客户端可用时,它将继续迭代任务。当没有可用的客户端时,它将阻塞(异步-不阻塞线程),直到以前的某个处理完成并将客户端发送回代理的邮箱:
let workloadAgent = MailboxProcessor.Start(fun agent ->
// The body of the agent, runs as a recursive loop that iterates
// over the tasks and waits for client before it starts processing
let rec loop tasks = async {
match tasks with
| [] ->
// No more work to schedule (but there may be still calculation,
// which was started earlier, going on in the background)
()
| task::tasks ->
// Wait for the next client to become available by receiving the
// next message from the inbox - the messages are clients
let! client = agent.Receive()
// Spanw processing of the task using the client
async { calculate client task
// When the processing completes, send the client
// back to the agent, so that it can be reused
agent.Post(client) }
|> Async.Start
// Continue processing the rest of the tasks
return! loop tasks }
// Start the agent with the initial list of tasks
loop tasks )
// Add all clients to the agent, so that it can start
for client in clients do workloadAgent.Post(client)
如果您不熟悉F#agents,那么MSDN部分提供了一些有用的信息。谢谢您,托马斯。像往常一样回答得很好。我没有意识到没有可变的集合是不可能的。这就是为什么C语言很容易,F语言很难的原因。我想知道Haskell的人是如何处理这种任务的?只有当我将
async{do!calculate client task
更改为async{do calculate client task
@Max时,它才会编译-是的,使用do calculate
是正确的(你甚至可以省略do
)。我假设calculate
也会是异步的,可能是因为它做了一些需要等待的工作,但我忘了在回答中提到这一点。@Max我想Haskeller会用Par
monad()来解决这个问题,但我不太确定如何解决。这可能是一个有趣的haskell
问题:-)