Multithreading f中线程阻塞到非线程阻塞的转换#

Multithreading f中线程阻塞到非线程阻塞的转换#,multithreading,asynchronous,f#,Multithreading,Asynchronous,F#,我从彭博API中检索数据,对速度的缓慢感到非常惊讶。 我的计算是有界的 因此,我决定使用一些异步monad构建器来解除它的束缚。 运行它后,结果并没有那么好,这是显而易见的,因为我调用了一个函数NextEvent,它是线程阻塞 let outerloop args dic = ... let rec innerloop continuetoloop = let eventObj = session.NextEvent();

我从彭博API中检索数据,对速度的缓慢感到非常惊讶。 我的计算是有界的

因此,我决定使用一些异步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
let outerloop args dic=
...
让rec innerloop继续循环=
让eventObj=session.NextEvent()//这个街区
...
让seqtable=reader.ReadFile(@“C:\homeware\sector.csv”,“;”[0],true)
设dic=ConcurrentDictionary()
让wf=seqtable |>Seq.mapi(乐趣i项->异步{outerloop项dic})
wf |>Async.Parallel
|>异步运行
|>忽略
打印fn“%A”ret
有没有一种好方法可以将阻塞调用包装为非阻塞调用? 另外,为什么异步框架创建的线程没有我请求的那么多(比如200个)?当我检查从中接收值的线程时,我只看到使用的4-5个线程

更新

我找到了一个令人信服的理由,解释了为什么它永远不可能实现。 异步操作获取异步指令之后的内容,并将其调度到线程池中的某个位置。 重要的是,只要异步函数正确使用,也就是说,总是返回到它起源的线程池,我们可以认为我们是在一个线程上执行的。 在单个线程上意味着所有的调度都将在稍后的某个地方执行,而阻塞指令无法避免这样一个事实,即一旦运行,它最终将不得不在将来的某个时间阻塞工作流

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

不可以。您永远不能将阻塞调用包装为非阻塞调用。如果可以的话,异步将只是一种设计模式,而不是根本的范式转换

另外,为什么异步框架创建的线程没有我请求的那么多(比如200个)

异步是建立在线程池的基础上的,线程池的设计目的是不积极地创建线程,因为它们非常昂贵。(真正的)异步的要点在于它不需要像连接那样多的线程。您可以用大约30个线程同时处理10000个连接

您似乎完全误解了异步是什么以及它的用途。我建议你买任何一本关于这个话题的书,然后仔细阅读。特别是,您的解决方案不是异步的,因为您只是从异步工作流内部调用blocking
StartGetFieldsValue
成员,这就违背了异步的目的。您最好只执行
Array.Parallel.map getFieldsValue

此外,在并行执行操作时,您希望使用纯函数式API,而不是在适当的位置修改
ConcurrentDictionary
。因此,将
req.startGetFields值ret
替换为

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

dict

替换
ignore
,这是我提出的一个似乎有效的解决方案。 当然,它不仅使用async(减去a),还使用async

我定义了一个简单类型,它有一个事件finished和一个方法asyncstart,并将该方法作为参数运行。它启动该方法,然后在适当的位置触发事件finished(在我的例子中,我必须捕获同步上下文等等)

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

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

在运行这段代码时,在用户端,我只使用异步调用,使代码无阻塞。当然,一定有一些线程被阻塞了,但这发生在我的主服务循环之外,它仍然是反应式的和合适的。

创建200个线程对您有什么帮助?如果您有200个连接,您将有200个非常慢的连接,而不仅仅是几个快速连接。另外,如果您的操作是IO阻塞,那么使用更多的CPU不会给您带来太多好处。它之所以慢,是因为网络速度慢,而且你不能通过使用更多线程来提高网络速度。@斯维克:对于一个IO绑定的计算,200个慢连接为什么不比5个慢连接好40倍呢?我可以看到,它现在正在处理40组5个操作,最好是处理1组200个操作。还是我遗漏了什么?因为你可能受到人际网络的限制。使用更多的线程不会使你的网络更快。哦,你是说每一次计算都会慢40倍。我对此表示怀疑,因为如果我进行分组检索,速度会快得多。所以主要是延迟,更多的带宽限制了我的通话。因此,只支付一次延迟肯定会提高总体速度。我不希望在第一次pb中使用异步解决方案。正如我所读到的,异步只是CPS重写。(对于字典来说,如果这个特定的API没有效率的话,那么可以通过几个字段请求几个证券,并且信息是一点一点来的。最小的有意义的单位是什么…可能有一种很好的打包方法,但我不知道)尽管如此,我肯定会阅读更多的材料,因为必须有一种方法可以很好地映射这个api。你的评论让我再次思考什么是真正的异步计算。虽然我们可以说它们只是CPS重写,但这只是代码的一种形式。如果我没有弄错的话,那么真正的情况是异步计算用于线程池,而同步计算用于线程。这就是为什么他们“感觉”像是大部分时间的同步计算。正如您所指出的,线程池在99%的时间里对我们的工作非常有用,异步构造反映了这种能力。线程池是实现F#中异步的一部分,但重要的区别是异步在