据我所知,Stream.cs异步操作的源代码是同步完成的,而不是完全并行完成(如果你读了一些东西,它会阻止其他读取尝试直到完成,并且还阻止写入attemts,同样以其他方式 - 写入块读取和写入尝试),它不会阻止调用writeAsync / readAsync的线程的唯一好处
所以我将TcpClient包装到接受
的MailboxProcessor中type AgentRequest<'a> =
| Write of 'a
| Read of int * AsyncReplyChannel<'a>
目的是在连接失败时重新连接客户端,在此期间我们不想尝试读取或写入任何内容。这也可以通过线程同步技术实现,但需要更多代码。
当我可以对解决方案进行基准测试时,我没有找到位置,但是有没有任何基于MailboxProcessor内部的基准来看看它对tcp连接会产生什么影响(如果整个请求/响应的性能影响很小)时间)
此外,这是为了向服务器发送请求,以确保响应顺序与收到的请求相同
但我不能依赖Write requestA
- &gt; Read responseA
阅读正确答案:
tread1:
写请求A.
阅读回复A.
tread2:
写请求B.
阅读回复B
队列:[&#39;写请求A&#39 ;; &#39;写请求B&#39 ;; &#39;阅读回复B&#39 ;; &#39;阅读回复A&#39;] 这将导致responseA将返回到thread2,而responseB将返回到thread1。
请求 - 响应的好处是通过请求中设置的id来关联。
因此存在一些存储Dictionary<id, TaskCompletionSource>
的协议的解决方案。这使得可以等到Task完成(TaskCompletionSource设置结果),然后继续结果。由单独的单个线程设置的结果,它保持从tcp流读取响应并通过id映射它们。
在F#中,使用Async而不是Task非常漂亮,因此我看到如何将响应映射到正确的请求的方式是存储Dictionary<id, response -> unit>
或者我只能为邮箱处理器发送一条消息,例如Send request
,并且不会将写请求与读取响应分开。
还有其他类似于TaskCompletionSource的方法,但是对于Async,我可以这样:
// instead of
async {
do! send request
sharedDictionary.Add(request.id, fun resp -> () (*do something with this response*) )
read() }
// do something like
async {
do! send request
let! response = read request.id
(*do something with this response*) }