在f#中将线程阻塞转换为非线程阻塞

时间:2012-03-28 08:58:17

标签: multithreading asynchronous f#

我从Bloomberg API检索数据,并且对这种缓慢感到非常惊讶。 我的计算是由IO限制的。

因此我决定使用一些异步monad构建器来取消它。 在运行它时,结果并没有那么好,这很明显,因为我调用一个函数NextEvent,它是线程阻塞。

     let outerloop args dic = 
        ...
        let rec innerloop continuetoloop   =
           let eventObj = session.NextEvent();  //This blocks
            ...

     let seqtable = reader.ReadFile( @"C:\homeware\sector.csv", ";".[0], true) 

     let dic = ConcurrentDictionary<_,_> ()
     let wf = seqtable |> Seq.mapi (fun i item -> async { outerloop item dic } )
     wf  |> Async.Parallel
         |> Async.RunSynchronously
         |> ignore
     printfn "%A" ret

是否有一种方法可以将阻止呼叫包含在非阻塞呼叫中? 另外,为什么异步框架没有创建我请求的线程(如200)?当我检查从中接收值的线程时,我看到只使用了4-5 ..

更新

我找到了一个令人信服的理由,说明为什么它永远不可能实现。 异步操作采用异步指令之后的内容并将其安排在线程池中的某个位置。 对于所有重要的事情,只要异步函数正确使用,即始终返回它源自的线程池,我们就可以认为我们是在一个线程上执行。

在单个线程上意味着所有调度将始终在某个地方执行,并且阻塞指令无法避免这样一个事实:一旦它运行,它将不得不在将来某个时候阻塞worflow。

2 个答案:

答案 0 :(得分:2)

  

有没有一种方法可以将阻塞调用包装到非阻塞调用?

没有。你永远不能包装阻塞调用,使它们无阻塞。如果可以的话,异步只是一种设计模式,而不是一种基本的范式转换。

  

另外,为什么异步框架没有创建我请求的线程数量(如200)?

Async建立在线程池之上,该线程池设计为不会因为它们如此昂贵而积极地创建线程。 (真正的)异步的全部意义在于它不需要与连接一样多的线程。您可以使用~30个线程处理10,000个并发连接。

您似乎完全误解了异步是什么以及它是什么。我建议购买任何涵盖这个主题的F#书籍并阅读它。特别是,您的解决方案不是异步的,因为您只是从异步工作流中调用阻塞StartGetFieldsValue成员,从而破坏了异步的目的。您也可以改为Array.Parallel.map getFieldsValue

此外,您希望在并行执行时使用纯函数API,而不是在原位变换ConcurrentDictionary。所以用{/ p>替换req.StartGetFieldsValue ret

let! result = req.StartGetFieldsValue()
...
return result

并将ignore替换为dict

答案 1 :(得分:0)

这是我制作的解决方案似乎正在发挥作用。 当然它不仅使用async(减去a),也使用Async。

我定义了一个简单类型,它有一个事件,完成,一个方法,asyncstart,以及作为参数运行的方法。它启动方法,然后在适当的地方触发事件(在我的情况下,我必须捕获同步上下文等..)

然后在消费者方面,我只使用

let! completion = Async.Waitfromevent wapper.finished |> Async.StartAsChild
let! completed = completion

在运行此代码时,在消费者方面,我只使用异步调用,使我的代码无阻塞。当然,某些线程必须被阻塞,但这发生在我的主服务循环之外,它仍然是反应性的。