I have the following situation in my recent "Play With Rx"-project:
class Program
{
static void Main(string[] args)
{
var observable1 = Observable.Create<int>(
(Func<IObserver<int>, IDisposable>)GenerateSequence);
var observable2 = Observable.Create<int>(
(Func<IObserver<int>, IDisposable>)GenerateSequence);
var merged = observable1.Merge(observable2);
observable1.Subscribe(i => Console.WriteLine("1: " + i.ToString()));
observable2.Subscribe(i => Console.WriteLine("2: " + i.ToString()));
merged.Subscribe(i => Console.WriteLine("Merged: " + i.ToString()));
Console.ReadLine();
}
private static int count = 0;
private static IDisposable GenerateSequence(IObserver<int> observer)
{
ThreadPool.QueueUserWorkItem((o) =>
{
while (true)
{
observer.OnNext(count++);
Thread.Sleep(500);
}
});
return Disposable.Empty;
}
}
Now, I expected to see something like
1: 0
2: 1
Merged: 0
Merged: 1
1: 2
2: 3
Merged: 2
Merged: 3
Instead I am seeing
1: 0
2: 1
Merged: 2
Merged: 3
1: 4
2: 5
Merged: 6
Merged: 7
If I replace the loop by
while (true)
{
observer.OnNext(r.Next(0, 1000));
Thread.Sleep(500);
}
for a either static or local instance r of Random, the merged sequence has other numbers in it that the two seperate sequences!
I do not see how count++
or r.Next(0, 1000)
can be executed multiple times from one call of observer.OnNext(...)
. What about Merge do I not understand?
PS: I've tried to eliminate race conditions by locks or seperating the loop times of the two threads, but the result was unchanged, so I left these attempts out of the question.
Edit: It seems that GenerateSequence
is called 4 times, so that 4 threads are spun up to increment count
. While this explains what I see, I do not understand why it should be so.
observable1
, you subscribe to Observable.Create(GenerateSequence)
, which calls GenerateSequence
and starts a loop. observable2
, you subscribe to Observable.Create(GenerateSequence)
, which calls GenerateSequence
and starts a loop. merged
, you subscribe to Observable.Merge(observable1, observable2)
, which subscribes to observable1
and observable2
. We saw in the first two points what happens when you do each of those. The net result is four calls to GenerateSequence
.
To get an effect pretty close to you're looking for, you need to look at Publish()
:
var observable1 = Observable
.Create<int>((Func<IObserver<int>, IDisposable>)GenerateSequence)
.Publish();
var observable2 = Observable
.Create<int>((Func<IObserver<int>, IDisposable>)GenerateSequence)
.Publish();
var merged = observable1.Merge(observable2);
observable1.Subscribe(i => Console.WriteLine("1: " + i.ToString()));
observable2.Subscribe(i => Console.WriteLine("2: " + i.ToString()));
merged.Subscribe(i => Console.WriteLine("Merged: " + i.ToString()));
observable1.Connect();
observable2.Connect();
observable1
and observable2
are now of type IConnectableObservable
, which means they hold off subscribing to their underlying IObservable
( Observable.Create
in your case) until they have Connect
called.
Output
1: 0
Merged: 0
2: 1
Merged: 1
1: 2
Merged: 2
2: 3
Merged: 3
...
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.