简体   繁体   English

IObservable ObserveOn 正在锁定线程,这是可以预防的吗?

[英]IObservable ObserveOn is locking the Thread, is this preventable?

I'm designing a server that diverts client requests to a single thread dedicated to handling data.我正在设计一个服务器,将客户端请求转移到一个专用于处理数据的线程。 I do this to prevent any race conditions or concurrency issues with the data being handled.我这样做是为了防止正在处理的数据出现任何竞争条件或并发问题。 Because the server is designed to be reactive, whenever the server receives a request I use Observables to notify the rest of the program of the request.因为服务器被设计为反应式的,所以每当服务器收到请求时,我都会使用 Observables 将请求通知给程序的其余部分。 Now because the server socket is listening and emitting signals from multiple threads I want to ensure that the observables, no matter what thread the server emits on, would always be observed on the dedicated data handling thread.现在因为服务器套接字正在侦听和发射来自多个线程的信号,我想确保无论服务器在哪个线程上发射,都可以在专用数据处理线程上观察到观察对象 I opted to use the ObserveOn method and this immediately backfired.我选择使用ObserveOn方法,这立即适得其反。 I noticed immediately that upon one observable firing, none of the others were firing.我立即注意到,在一次可观察到的射击时,其他人都没有射击。 Not only that, but other actions sent to the dedicated thread weren't firing either.不仅如此,发送到专用线程的其他操作也没有触发。

Essentially, the observable seems to be "claiming" the thread for itself.本质上,可观察对象似乎在为自己“声明”线程。 The thread is completely blocked by the observable and cannot be used for anything else at all other than that observable's emissions.该线程完全被 observable 阻塞,除了该 observable 的排放之外,不能用于任何其他事情。 I don't want this happening because this thread is dedicated to all data handling operations, and this is stopping me from using the thread for any other observables or future data handling tasks.我不希望这种情况发生,因为该线程专用于所有数据处理操作,这阻止了我将该线程用于任何其他可观察对象或未来的数据处理任务。 So, what are my options here to prevent the observable locking the thread down to itself, or to force observation of observables to my dedicated thread without blocking out other observables.那么,我在这里有什么选择可以防止可观察对象将线程锁定到自身,或者强制将可观察对象观察到我的专用线程而不阻塞其他可观察对象。

This example code demonstrates the problem.此示例代码演示了该问题。 Here we use a single threaded task scheduler and notice that it operates just fine until the first subject, which has been set to ObserveOn the scheduler, emits it's string.这里我们使用一个单线程任务调度程序,并注意到它运行得很好,直到第一个主题(已设置为ObserveOn调度程序)发出它的字符串。 After this happens, no further subject or action fires.发生这种情况后,不会再触发任何主题或动作。 The first subject effectively locked the thread down for itself.第一个主题有效地为自己锁定了线程。

public static class Program
{
    static void Main(string[] args)
    {
        //Within the Tester class we setup a single threaded task scheduler that will be handling all of these methods
        var _t = new Tester();

        string _string = "Hello World";

        //These three will print their string to the console
        _t.PrintDirectlyWithAction(_string);//Succeeds
        _t.PrintDirectlyWithAction(_string);//Succeeds
        _t.PrintDirectlyWithAction(_string);//Succeeds

        //Only subject 1 will emit and print it's string, the other two fail
        _t.PrintThroughSubject1(_string);//Succeeds
        _t.PrintThroughSubject2(_string);//Fails
        _t.PrintThroughSubject3(_string);//Fails

        _t.PrintDirectlyWithAction(_string);//Fails
        _t.PrintDirectlyWithAction(_string);//Fails
        _t.PrintDirectlyWithAction(_string);//Fails

        //We essentially can't do anything with the thread after subject 1 observed on it

        Console.ReadLine();
    }

    public class Tester
    {
        TaskFactory tf;
        TaskPoolScheduler pool;
        int _actionCount = 0;
        Subject<string> s1 = new Subject<string>();
        Subject<string> s2 = new Subject<string>();
        Subject<string> s3 = new Subject<string>();

        public Tester()
        {
            //We're create a task pool that uses a single threaded concurrent task scheduler
            var _scheduler = new ConcurrentExclusiveSchedulerPair();
            tf = new TaskFactory(_scheduler.ExclusiveScheduler);
            pool = new TaskPoolScheduler(tf);

            //And then we set the subjects to each be observed on the single threaded scheduler
            s1.ObserveOn(pool).Subscribe(_s => Console.WriteLine(
                $"Subject (1) says \"{_s}\" - on thread {Thread.CurrentThread.ManagedThreadId}"));
            s2.ObserveOn(pool).Subscribe(_s => Console.WriteLine(
                $"Subject (2) says \"{_s}\" - on thread {Thread.CurrentThread.ManagedThreadId}"));
            s3.ObserveOn(pool).Subscribe(_s => Console.WriteLine(
                $"Subject (3) says \"{_s}\" - on thread {Thread.CurrentThread.ManagedThreadId}"));
        }

        public void PrintThroughSubject1(string _string)
        {
            s1.OnNext(_string);
        }

        public void PrintThroughSubject2(string _string)
        {
            s2.OnNext(_string);
        }

        public void PrintThroughSubject3(string _string)
        {
            s3.OnNext(_string);
        }

        public void PrintDirectlyWithAction(string _string)
        {
            //This is here to demonstrate that the single threaded task scheduler accepts actions just fine
            //and can handle them in sequence
            tf.StartNew(() =>
            {
                Console.WriteLine(
                    $"Direct action ({_actionCount++}) says \"{_string}\" - on thread {Thread.CurrentThread.ManagedThreadId}");
            });
        }

    }
}

TL;DR: I need to be able to force multiple observables emissions to be observed on a specific thread, but RxNet seems to only be letting a single subject be observed on a thread and nothing else can. TL;DR:我需要能够强制在特定线程上观察多个可观察的发射,但 RxNet 似乎只允许在一个线程上观察单个主题,而没有其他任何东西可以。 How can I circumvent this to observe multiple observables on the same thread?我怎样才能绕过这个来观察同一线程上的多个 observables?

I might have over complicated it.我可能把它复杂化了。 EventLoopScheduler might be what you need. EventLoopScheduler可能正是您所需要的。

Try this:试试这个:

public static class Program
{
    static void Main(string[] args)
    {
        //Within the Tester class we setup a single threaded task scheduler that will be handling all of these methods
        var _t = new Tester();

        string _string = "Hello World";

        //These three will print their string to the console
        _t.PrintDirectlyWithAction(_string);//Succeeds
        _t.PrintDirectlyWithAction(_string);//Succeeds
        _t.PrintDirectlyWithAction(_string);//Succeeds

        //Only subject 1 will emit and print it's string, the other two fail
        _t.PrintThroughSubject1(_string);//Succeeds
        _t.PrintThroughSubject2(_string);//Fails
        _t.PrintThroughSubject3(_string);//Fails

        _t.PrintDirectlyWithAction(_string);//Fails
        _t.PrintDirectlyWithAction(_string);//Fails
        _t.PrintDirectlyWithAction(_string);//Fails

        //We essentially can't do anything with the thread after subject 1 observed on it

        Console.ReadLine();
    }

    public class Tester
    {
        private EventLoopScheduler els = new EventLoopScheduler();
        int _actionCount = 0;
        Subject<string> s1 = new Subject<string>();
        Subject<string> s2 = new Subject<string>();
        Subject<string> s3 = new Subject<string>();

        public Tester()
        {
            //We're create a task pool that uses a single threaded concurrent task scheduler


            //And then we set the subjects to each be observed on the single threaded scheduler
            s1.ObserveOn(els).Subscribe(_s => Console.WriteLine(
                $"Subject (1) says \"{_s}\" - on thread {Thread.CurrentThread.ManagedThreadId}"));
            s2.ObserveOn(els).Subscribe(_s => Console.WriteLine(
                $"Subject (2) says \"{_s}\" - on thread {Thread.CurrentThread.ManagedThreadId}"));
            s3.ObserveOn(els).Subscribe(_s => Console.WriteLine(
                $"Subject (3) says \"{_s}\" - on thread {Thread.CurrentThread.ManagedThreadId}"));
        }

        public void PrintThroughSubject1(string _string)
        {
            s1.OnNext(_string);
        }

        public void PrintThroughSubject2(string _string)
        {
            s2.OnNext(_string);
        }

        public void PrintThroughSubject3(string _string)
        {
            s3.OnNext(_string);
        }

        public void PrintDirectlyWithAction(string _string)
        {
            //This is here to demonstrate that the single threaded task scheduler accepts actions just fine
            //and can handle them in sequence
            els.Schedule(() =>
            {
                Console.WriteLine(
                    $"Direct action ({_actionCount++}) says \"{_string}\" - on thread {Thread.CurrentThread.ManagedThreadId}");
            });
        }

    }
}

I get this result:我得到这个结果:

Direct action (0) says "Hello World" - on thread 17
Direct action (1) says "Hello World" - on thread 17
Direct action (2) says "Hello World" - on thread 17
Subject (1) says "Hello World" - on thread 17
Subject (2) says "Hello World" - on thread 17
Subject (3) says "Hello World" - on thread 17
Direct action (3) says "Hello World" - on thread 17
Direct action (4) says "Hello World" - on thread 17
Direct action (5) says "Hello World" - on thread 17

Don't forget to .Dispose() your EventLoopScheduler when you're done.完成后不要忘记.Dispose()你的EventLoopScheduler

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

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