Multithreading f中线程阻塞到非线程阻塞的转换#
我从彭博API中检索数据,对速度的缓慢感到非常惊讶。 我的计算是有界的 因此,我决定使用一些异步monad构建器来解除它的束缚。 运行它后,结果并没有那么好,这是显而易见的,因为我调用了一个函数NextEvent,它是线程阻塞Multithreading f中线程阻塞到非线程阻塞的转换#,multithreading,asynchronous,f#,Multithreading,Asynchronous,F#,我从彭博API中检索数据,对速度的缓慢感到非常惊讶。 我的计算是有界的 因此,我决定使用一些异步monad构建器来解除它的束缚。 运行它后,结果并没有那么好,这是显而易见的,因为我调用了一个函数NextEvent,它是线程阻塞 let outerloop args dic = ... let rec innerloop continuetoloop = let eventObj = session.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个连接
您似乎完全误解了异步是什么以及它的用途。我建议你买任何一本关于这个话题的书,然后仔细阅读。特别是,您的解决方案不是异步的,因为您只是从异步工作流内部调用blockingStartGetFieldsValue
成员,这就违背了异步的目的。您最好只执行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#中异步的一部分,但重要的区别是异步在