C# 获取第一个IObservable事件,而不阻塞需要它的线程/任务

C# 获取第一个IObservable事件,而不阻塞需要它的线程/任务,c#,multithreading,reactive,C#,Multithreading,Reactive,我正在考虑使用IObservable在c#async方法中的请求-响应环境中获得响应,并替换一些旧的基于回调的代码,但我发现如果将值(Subject.OnNext)推送到可观察的,但FirstAsync尚未等待,然后,FirstAsync永远不会收到该消息 没有第二个任务/线程加上同步,有没有一种简单的方法可以让它工作 public async Task<ResponseMessage> Send(RequestMessage message) { var id = Guid

我正在考虑使用
IObservable
在c#
async
方法中的请求-响应环境中获得响应,并替换一些旧的基于回调的代码,但我发现如果将值(
Subject.OnNext
)推送到可观察的,但
FirstAsync
尚未等待,然后,
FirstAsync
永远不会收到该消息

没有第二个任务/线程加上同步,有没有一种简单的方法可以让它工作

public async Task<ResponseMessage> Send(RequestMessage message)
{
    var id = Guid.NewGuid();
    var ret = Inbound.FirstAsync((x) => x.id == id).Timeout(timeout); // Never even gets invoked if response is too fast
    await DoSendMessage(id, message);
    return await ret; // Will sometimes miss the event/message
}

// somewhere else reading the socket in a loop
// may or may not be the thread calling Send
Inbound = subject.AsObservable();
while (cond)
{
    ...
    subject.OnNext(message);
}
公共异步任务发送(请求消息)
{
var id=Guid.NewGuid();
var ret=Inbound.FirstAsync((x)=>x.id==id).Timeout(Timeout);//如果响应太快,甚至都不会被调用
等待DoSendMessage(id,消息);
return wait ret;//有时会错过事件/消息
}
//在其他地方读取循环中的套接字
//可能是也可能不是调用Send的线程
Inbound=subject.AsObservable();
while(cond)
{
...
subject.OnNext(消息);
}

我不能在发送请求之前简单地将
wait
用于
FirstAsync
,因为这会阻止请求被发送。

等待
wait
将订阅可观察的。您可以通过调用ToTask,将订阅与Wait分开:

public async Task<ResponseMessage> Send(RequestMessage message)
{
  var id = Guid.NewGuid();
  var ret = Inbound.FirstAsync((x) => x.id == id).Timeout(timeout).ToTask();
  await DoSendMessage(id, message);
  return await ret;
}
公共异步任务发送(请求消息)
{
var id=Guid.NewGuid();
var ret=Inbound.FirstAsync((x)=>x.id==id).Timeout(Timeout.ToTask();
等待DoSendMessage(id,消息);
返回等待返回;
}

我仔细观察了一下,通过将热的可见光转换为冷的可见光,可以很容易地解决您的问题。将
主题
替换为
重放主题
。以下是文章:

解释如下:

Replay扩展方法允许您获取现有的可观测数据 按照ReplaySubject对其进行排序并给出“replay”语义。作为一个 提醒,ReplaySubject将缓存所有值,以便 订阅者还将获得所有值


使用TaskCompletionSource是一个选项?它看起来像什么?比如说,设置一个设置tcs结果的订阅,然后取消订阅本身。然后发送然后等待tcs?猜是这样吗?描述似乎并不能真正解释这一点。但是我要理解的是,
Where
FirstAsync
等的链就像是“挂起”,而
ToTask
wait
,(或其他东西)使其处于活动状态并开始观察事件?是的。与其他LINQ构造一样,Rx使用其运算符建立一个“定义”,并且它仅在订阅时执行(在本例中,
等待
观察,或转换为
任务
)。