简体   繁体   中英

Create IO bound observable with RX

I have a worker thread making blocking calls ( ReadFrame ) to read incoming data from a socket (IO bound). The thread runs a loop, that feeds the data into an Subject , that can be observed by consumers.

private void ReadLoop()
{
    while (!IsDisposed)
    {
        var frame = _Socket.ReadFrame();
        _ReceivedFrames.OnNext(frame);
    }
}

I wonder if there is a more RX kind of way to do this.

Here is an attempt (toy example) I made:

var src = Observable
         .Repeat(Unit.Default)
         .Select(_ =>
         {
             Thread.Sleep(1000);              // simulated blocking ReadFrame call
             return "data read from socket";
         })
         .SubscribeOn(ThreadPoolScheduler.Instance) // avoid blocking when subscribing
         .ObserveOn(NewThreadScheduler.Default)     // spin up new thread (?)
         .Publish()
         .RefCount();

var d = src.Subscribe(s => s.Dump()); // simulated consumer

Console.ReadLine();  // simulated main running code

d.Dispose();  // tear down

I'm struggling with the correct use of ObserveOn , SubscribeOn and the schedulers. The toy example seems to work, but I'm not sure if the thread's lifetime is managed correctly.

Is the reader thread shut down with the d.Dispose() call?
Do I need to create a new thread at all?
Should I use Observable.Create instead? How?

Below additional info requested by @Enigmativity:

The ReadLoop() method is part of a class that conforms to the following interface:

public interface ICanSocket : IDisposable
{
    IObservable<CanFrame> ReceivedFrames { get; }
    IObserver<CanFrame>   FramesToSend   { get; }
}

Its member _Socket is disposed (closed) when the parent ICanSocket is disposed.

The most "Rxy" way to do this is to use Rxx , which has observable methods for doing async I/O.

Seems like your primary concerns are:

  • When subscribing, do not block the subscriber thread (aka run the I/O thread on a background thread)
  • When the caller unsubscribes, stop the I/O thread

One way to solve these is to just use the async Create method:

// just use Task.Run to "background" the work
var src = Observable
    .Create<CanFrame>((observer, cancellationToken) => Task.Run(() =>
    {
        while (!cancellationToken.IsCancellationRequested)
        {
            var frame = _Socket.ReadFrame();
            if (frame == null) // end of stream?
            {
                // will send a Completed event
                return;
            }

            observer.OnNext(frame);
        }
    }));

var d = src.Subscribe(s => s.Dump());
Console.ReadLine();
d.Dispose();

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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