Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/fsharp/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Asynchronous 分配工作负载_Asynchronous_F# - Fatal编程技术网

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
问题:-)