Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
129 views
in Technique[技术] by (71.8m points)

c# - NewThreadScheduler.Default schedules all work on same thread

I'm currently trying to wrap my head around concurrency with RX .NET and getting confused by something. I want to run four relatively slow tasks in parallel, so I assumed NewThreadScheduler.Default would be the way to go, as it "Represents an object that schedules each unit of work on a separate thread.".

Here's my setup code:

    static void Test()
    {
        Console.WriteLine("Starting. Thread {0}", Thread.CurrentThread.ManagedThreadId);

        var query = Enumerable.Range(1, 4);
        var obsQuery = query.ToObservable(NewThreadScheduler.Default);
        obsQuery.Subscribe(DoWork, Done);

        Console.WriteLine("Last line. Thread {0}", Thread.CurrentThread.ManagedThreadId);
    }

    static void DoWork(int i)
    {
        Thread.Sleep(500);
        Console.WriteLine("{0} Thread {1}", i, Thread.CurrentThread.ManagedThreadId);
    }

    static void Done()
    {
        Console.WriteLine("Done. Thread {0}", Thread.CurrentThread.ManagedThreadId);
    }

I assumed the "X Thread Y" would output a different thread id every time, however the actual output is:

Starting. Thread 1
Last line. Thread 1
1 Thread 3
2 Thread 3
3 Thread 3
4 Thread 3
Done. Thread 3

All the work is being one on the same new thread in sequential order, which isn't what I was expecting.

I'm assuming I'm missing something, but I can't figure out what.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

There are two parts to an observable query, the Query itself and the Subscription. (This is also the difference between the ObserveOn and SubscribeOn operators.)

Your Query is

Enumerable
    .Range(1, 4)
    .ToObservable(NewThreadScheduler.Default);

This creates an observable that produces values on the default NewThreadScheduler for that system.

Your Subscription is

obsQuery.Subscribe(DoWork, Done);

This runs DoWork for each value produced by the Query and Done when the Query finishes with an OnComplete call. I don't think there are any guarantees about what thread the functions in the subscribe method will be called on, in practice if all values of the query are produced on the same thread that is the thread the subscription will be run on. It appears they are also making it so all of the subscription calls are made on the same thread which is most likely done to get rid of a lot of common multi-threading errors.

So you have two issues, one is with your logging, if you change your Query to

Enumerable
    .Range(1, 4)
    .Do(x => Console.WriteLine("Query Value {0} produced on Thread {1}", x, Thread.CurrentThread.ManagedThreadId);
    .ToObservable(NewThreadScheduler.Default);

You'll see each value produced on a new thread.

The other issue is one of the intention and design of Rx. It's intended that the Query is the long-running process and the Subscription is a short method that deals with the results. If you want to run a long running function as an Rx Observable your best option is to use Observable.ToAsync.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...