简体   繁体   English

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

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

I'm trying to listen for some connections using the standard System.Net Socket API and I'm planning on using Reactive Extensions to bridge the gap and create a intuitive way of listening to aforementioned connectionss. 我正在尝试使用标准的System.Net Socket API监听某些连接,并且我计划使用反应性扩展来缩小差距并创建一种侦听上述连接的直观方式。

Here's my code so far: 到目前为止,这是我的代码:

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

Now, the socket listening IS working as planned - I'm receiving connections no problem. 现在,套接字监听按计划工作-我收到的连接没有问题。 Issue is, is that the first connection is being received more than once (ie, it appears Observable.FromAsync is caching the result of the async task object). 问题是,第一个连接被接收了多次(即,它似乎是Observable.FromAsync正在缓存异步task对象的结果)。 I know this is obviously due to the Repeat() statement but I was under the impression that wrapping Observable.FromAsync inside of Observable.Defer and then invoking the Repeat on the deferred observable would circumvent the caching - what am I doing wrong? 我知道这是很明显,由于Repeat()语句,但我的印象是包装Observable.FromAsyncObservable.Defer ,然后调用Repeat的递延观察到会绕过高速缓存-我究竟做错了什么?

Subscription code is simply: 订阅代码很简单:

listener
    .Connections
    .Subscribe(OnNewConnection);

Where listener.Connections is a property on an instance of RxConnectionListener called Connections which is backed by _connections 其中listener.ConnectionsRxConnectionListener实例(称为Connections )的属性,该实例由_connections支持

OnNewConnection is as follows: OnNewConnection如下:

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

Observed (pun intended) output after trying to connect via TCP once: 尝试通过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)

Edit: for completeness, I'm using the EventLoopScheduler , although commenting out the ObserveOn calls makes no difference. 编辑:为了完整EventLoopScheduler ,我正在使用EventLoopScheduler ,尽管注释掉ObserveOn调用没有什么区别。

By writing 通过写

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

You created a task getting the next socket connected. 您创建了一个任务,以连接下一个套接字。 If you ask for the task's result twice, you will get the same result both times. 如果您两次要求任务的结果,那么两次您都会得到相同的结果。 That's normal: a task always behaves this way: it runs until it completes, then "caches" its result (be it a normal ending or an exception). 这是正常的:任务总是以这种方式运行:它一直运行到完成为止,然后“缓存”其结果(正常结束还是异常)。

What you meant to do was tu create a function that creates a Task, like this: 您要做的是创建一个创建Task的函数,如下所示:

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

Now, you can create an observable from this Task factory easily: Observable.FromAsyn(acceptTask) 现在,您可以轻松地从此Task工厂创建一个可观察对象: Observable.FromAsyn(acceptTask)

Note that it is probably a bad idea to create an observable from a task that you created from the async pattern: there are methods to create the Observable directly from the pattern: save the creation of observables from tasks to the cases where the operations you want to create an observable from are already tasks. 请注意,从异步模式创建的任务中创建可观察对象可能不是一个好主意:有些方法可以直接从该模式创建可观察对象:将任务中可观察对象的创建保存到所需操作的情况下从已有任务创建可观察对象。

I'm quite sure that the Observable.FromAsync would cache the Result property of the task you're building, therefore the repeat will just give you back the result from the task instead of a new observable every time. 我非常确定Observable.FromAsync将缓存正在构建的任务的Result属性,因此重复执行只会使您返回任务的结果,而不是每次都返回新的Observable。 In order to create a valid repeatable connection you will need to rebuild your task every time. 为了创建有效的可重复连接,您每次都需要重建任务。 This way the socket result will not be cached anymore. 这样,套接字结果将不再被缓存。

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