簡體   English   中英

IObservable ObserveOn 正在鎖定線程,這是可以預防的嗎?

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

我正在設計一個服務器,將客戶端請求轉移到一個專用於處理數據的線程。 我這樣做是為了防止正在處理的數據出現任何競爭條件或並發問題。 因為服務器被設計為反應式的,所以每當服務器收到請求時,我都會使用 Observables 將請求通知給程序的其余部分。 現在因為服務器套接字正在偵聽和發射來自多個線程的信號,我想確保無論服務器在哪個線程上發射,都可以在專用數據處理線程上觀察到觀察對象 我選擇使用ObserveOn方法,這立即適得其反。 我立即注意到,在一次可觀察到的射擊時,其他人都沒有射擊。 不僅如此,發送到專用線程的其他操作也沒有觸發。

本質上,可觀察對象似乎在為自己“聲明”線程。 該線程完全被 observable 阻塞,除了該 observable 的排放之外,不能用於任何其他事情。 我不希望這種情況發生,因為該線程專用於所有數據處理操作,這阻止了我將該線程用於任何其他可觀察對象或未來的數據處理任務。 那么,我在這里有什么選擇可以防止可觀察對象將線程鎖定到自身,或者強制將可觀察對象觀察到我的專用線程而不阻塞其他可觀察對象。

此示例代碼演示了該問題。 這里我們使用一個單線程任務調度程序,並注意到它運行得很好,直到第一個主題(已設置為ObserveOn調度程序)發出它的字符串。 發生這種情況后,不會再觸發任何主題或動作。 第一個主題有效地為自己鎖定了線程。

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:我需要能夠強制在特定線程上觀察多個可觀察的發射,但 RxNet 似乎只允許在一個線程上觀察單個主題,而沒有其他任何東西可以。 我怎樣才能繞過這個來觀察同一線程上的多個 observables?

我可能把它復雜化了。 EventLoopScheduler可能正是您所需要的。

試試這個:

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

    }
}

我得到這個結果:

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

完成后不要忘記.Dispose()你的EventLoopScheduler

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM