为什么我的Observable'缓存'是异步操作的结果?

时间:2014-08-05 12:59:23

标签: c# system.reactive

我正在尝试使用标准的System.Net Socket API来监听某些连接,并且我计划使用Reactive Extensions弥合差距并创建一种直观的方式来聆听上述连接。

到目前为止,这是我的代码:

public RxConnectionListener(int port, Socket socket, IScheduler scheduler)
{
    _socket = socket;
    // TODO: Lazy binding?
    _socket.Bind(new IPEndPoint(IPAddress.Any, port));
    _socket.Listen(0);


    var task = Task.Factory.FromAsync(
        socket.BeginAccept,
        result => socket.EndAccept(result),
        null);

    _connections = Observable.Defer(() => Observable.FromAsync(() => task)
        ).Select(s => new RxConnection(s))
            .ObserveOn(scheduler)
            .Repeat();
}

现在,套接字监听IS按计划工作 - 我接收连接没问题。问题是,第一个连接被多次接收(即,Observable.FromAsync正在缓存异步task对象的结果。我知道这显然是由Repeat()语句引起的,但我的印象是将Observable.FromAsync包装在Observable.Defer内,然后在延迟的observable上调用Repeat会绕过缓存 - 我做错了什么?

订阅代码很简单:

listener
    .Connections
    .Subscribe(OnNewConnection);

其中listener.Connections是名为RxConnectionListener的{​​{1}}实例的属性,由Connections支持

_connections如下:

OnNewConnection

尝试通过TCP连接后观察到(双关语)输出:

protected virtual void OnNewConnection(IConnection connection)
{
    Console.WriteLine(connection.RemoteAddress);
}

编辑:为了完整起见,我使用了::ffff:127.0.0.1 ::ffff:127.0.0.1 ::ffff:127.0.0.1 ::ffff:127.0.0.1 .. (to infinity and beyond) ,虽然评论EventLoopScheduler次来电没有任何区别。

2 个答案:

答案 0 :(得分:1)

写作

var task = Task.Factory.FromAsync(
    socket.BeginAccept,
    result => socket.EndAccept(result),
    null);

您创建了一个任务,可以连接下一个套接字。如果您要求两次任务结果,则两次都会得到相同的结果。这是正常的:任务总是以这种方式运行:它一直运行直到它完成,然后"缓存"它的结果(无论是正常的结尾还是例外)。

你打算做的是创建一个创建任务的函数,如下所示:

Func<Task<Socket>> acceptTask = () =>
{
    return Task.Factory.FromAsync(
        socket.BeginAccept,
        result => socket.EndAccept(result),
        null);
};

现在,您可以轻松地从此任务工厂创建一个observable:Observable.FromAsyn(acceptTask)

请注意,从您从异步模式创建的任务创建一个observable可能是一个坏主意:有一些方法可以直接从模式创建Observable:将可观察项的创建从任务保存到其中的您想要创建可观察对象的操作已经是任务。

答案 1 :(得分:0)

我非常确定Observable.FromAsync会缓存您正在构建的任务的Result属性,因此重复只会让您从任务中返回结果,而不是每次都返回一个新的observable 。 为了创建有效的可重复连接,您需要每次都重建您的任务。这样就不会再缓存套接字结果了。

var _connections = Observable.Defer(() => Observable.FromAsync(() => 
    Task.Factory.FromAsync(
    socket.BeginAccept,
    result => socket.EndAccept(result),
    null)))
.Select(s => new RxConnection(s))
.Repeat();