繁体   English   中英

为什么可观察的“缓存”是异步操作的结果?

[英]Why is my Observable 'caching' the result of an Async operation?

我正在尝试使用标准的System.Net Socket API监听某些连接,并且我计划使用反应性扩展来缩小差距并创建一种侦听上述连接的直观方式。

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

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();
}

现在,套接字监听按计划工作-我收到的连接没有问题。 问题是,第一个连接被接收了多次(即,它似乎是Observable.FromAsync正在缓存异步task对象的结果)。 我知道这是很明显,由于Repeat()语句,但我的印象是包装Observable.FromAsyncObservable.Defer ,然后调用Repeat的递延观察到会绕过高速缓存-我究竟做错了什么?

订阅代码很简单:

listener
    .Connections
    .Subscribe(OnNewConnection);

其中listener.ConnectionsRxConnectionListener实例(称为Connections )的属性,该实例由_connections支持

OnNewConnection如下:

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

尝试通过TCP连接一次后,观察到的(双关语意为)输出:

::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 ,我正在使用EventLoopScheduler ,尽管注释掉ObserveOn调用没有什么区别。

通过写

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

您创建了一个任务,以连接下一个套接字。 如果您两次要求任务的结果,那么两次您都会得到相同的结果。 这是正常的:任务总是以这种方式运行:它一直运行到完成为止,然后“缓存”其结果(正常结束还是异常)。

您要做的是创建一个创建Task的函数,如下所示:

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

现在,您可以轻松地从此Task工厂创建一个可观察对象: Observable.FromAsyn(acceptTask)

请注意,从异步模式创建的任务中创建可观察对象可能不是一个好主意:有些方法可以直接从该模式创建可观察对象:将任务中可观察对象的创建保存到所需操作的情况下从已有任务创建可观察对象。

我非常确定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();

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM