[英]Observable not reacting to queue changed on different thread
我有以下代码:
static void Main()
{
var holderQueue = new ConcurrentQueue<int>(GetInitialElements());
Action<ConcurrentQueue<int>> addToQueueAction = AddToQueue;
var observableQueue = holderQueue.ToObservable();
IScheduler newThreadScheduler = new NewThreadScheduler();
IObservable<Timestamped<int>> myQueueTimestamped = observableQueue.Timestamp();
var bufferedTimestampedQueue = myQueueTimestamped.Buffer(TimeSpan.FromSeconds(3), TimeSpan.FromSeconds(3), newThreadScheduler);
var t = new TaskFactory();
t.StartNew(() => addToQueueAction(holderQueue));
using(bufferedTimestampedQueue.SubscribeOn(newThreadScheduler).Subscribe(currentQueue =>
{
Console.WriteLine("buffer time elapsed, current queue contents is: {0} items.", currentQueue.Count);
foreach(var item in currentQueue)
Console.WriteLine("item {0} at {1}", item.Value, item.Timestamp);
Console.WriteLine("holderqueue has: {0}", currentQueue.Count);
}))
{
Console.WriteLine("started observing queue");
Console.ReadLine();
}
}
private static void AddToQueue(ConcurrentQueue<int> concurrentQueue)
{
while(true)
{
var x = new Random().Next(1, 10);
concurrentQueue.Enqueue(x);
Console.WriteLine("added {0}", x);
Console.WriteLine("crtcount is: {0}", concurrentQueue.Count);
Thread.Sleep(1000);
}
}
private static IEnumerable<int> GetInitialElements()
{
var random = new Random();
var items = new List<int>();
for (int i = 0; i < 10; i++)
items.Add(random.Next(1, 10));
return items;
}
目的如下:
最初使用一些元素( GetInitialElements
)填充holderQueue
对象,然后在其他线程上使用其他元素(通过AddToQueue
方法)对其进行更改,并且AddToQueue
应该检测到此更改,并在经过其时间后做出相应的反应(因此每个3秒),方法是执行其订阅中的方法。
简而言之,我期望的是在Subscribe
主体中包含代码以每3秒执行一次,并向我展示队列中的更改(该更改在另一个线程上进行了更改)。 而是, Subscribe
主体仅执行一次。 为什么?
谢谢
ToObservable
方法采用IEnumerable<T>
并将其转换为可观察的。 结果,它将使用您的并发队列并立即枚举它,遍历所有可用项目。 您稍后修改队列以添加其他项的事实不会影响从并发队列的GetEnumerator()
实现返回的已枚举的IEnumerable<T>
。
根据David Pfeffer的回答,仅使用.ToObserverable()
并不能满足您的需求。
但是,当我查看您的代码时,会看到几件事:
NewThreadScheduler
ConcurrentQueue<T>
我认为,只要更改一些内容,您就可以实现您打算在这里进行的工作。 首先,我认为您实际上是在寻找BlockingCollection<T>
。 我知道这似乎不太可能,但是您可以使其像线程安全队列一样工作。
接下来,您已经在使用NewThreadScheduler
专用线程,为什么不从队列中进行轮询/拉取呢?
最后,如果您使用BlockingCollection<T>.GetConsumingEnumerable(CancellationToken)
方法,则实际上您可以返回并使用该.ToObservable()
方法!
因此,让我们看一下重写的代码:
static void Main()
{
//The processing thread. I try to set the the thread name as these tend to be long lived. This helps logs and debugging.
IScheduler newThreadScheduler = new NewThreadScheduler(ts=>{
var t = new Thread(ts);
t.Name = "QueueReader";
t.IsBackground = true;
return t;
});
//Provide the ability to cancel our work
var cts = new CancellationTokenSource();
//Use a BlockingCollection<T> instead of a ConcurrentQueue<T>
var holderQueue = new BlockingCollection<int>();
foreach (var element in GetInitialElements())
{
holderQueue.Add(element);
}
//The Action that periodically adds items to the queue. Now has cancellation support
Action<BlockingCollection<int>,CancellationToken> addToQueueAction = AddToQueue;
var tf = new TaskFactory();
tf.StartNew(() => addToQueueAction(holderQueue, cts.Token));
//Get a consuming enumerable. MoveNext on this will remove the item from the BlockingCollection<T> effectively making it a queue.
// Calling MoveNext on an empty queue will block until cancelled or an item is added.
var consumingEnumerable = holderQueue.GetConsumingEnumerable(cts.Token);
//Now we can make this Observable, as the underlying IEnumerbale<T> is a blocking consumer.
// Run on the QueueReader/newThreadScheduler thread.
// Use CancelationToken instead of IDisposable for single method of cancellation.
consumingEnumerable.ToObservable(newThreadScheduler)
.Timestamp()
.Buffer(TimeSpan.FromSeconds(3), TimeSpan.FromSeconds(3), newThreadScheduler)
.Subscribe(buffer =>
{
Console.WriteLine("buffer time elapsed, current queue contents is: {0} items.", buffer.Count);
foreach(var item in buffer)
Console.WriteLine("item {0} at {1}", item.Value, item.Timestamp);
Console.WriteLine("holderqueue has: {0}", holderQueue.Count);
},
cts.Token);
Console.WriteLine("started observing queue");
//Run until [Enter] is pressed by user.
Console.ReadLine();
//Cancel the production of values, the wait on the consuming enumerable and the subscription.
cts.Cancel();
Console.WriteLine("Cancelled");
}
private static void AddToQueue(BlockingCollection<int> input, CancellationToken cancellationToken)
{
while(!cancellationToken.IsCancellationRequested)
{
var x = new Random().Next(1, 10);
input.Add(x);
Console.WriteLine("added '{0}'. Count={1}", x, input.Count);
Thread.Sleep(1000);
}
}
private static IEnumerable<int> GetInitialElements()
{
var random = new Random();
var items = new List<int>();
for (int i = 0; i < 10; i++)
items.Add(random.Next(1, 10));
return items;
}
现在,我认为您将获得预期的结果:
added '9'. Count=11
started observing queue
added '4'. Count=1
added '8'. Count=1
added '3'. Count=1
buffer time elapsed, current queue contents is: 14 items.
item 9 at 25/01/2015 22:25:35 +00:00
item 5 at 25/01/2015 22:25:35 +00:00
item 5 at 25/01/2015 22:25:35 +00:00
item 9 at 25/01/2015 22:25:35 +00:00
item 7 at 25/01/2015 22:25:35 +00:00
item 6 at 25/01/2015 22:25:35 +00:00
item 2 at 25/01/2015 22:25:35 +00:00
item 2 at 25/01/2015 22:25:35 +00:00
item 9 at 25/01/2015 22:25:35 +00:00
item 3 at 25/01/2015 22:25:35 +00:00
item 9 at 25/01/2015 22:25:35 +00:00
item 4 at 25/01/2015 22:25:36 +00:00
item 8 at 25/01/2015 22:25:37 +00:00
item 3 at 25/01/2015 22:25:38 +00:00
holderqueue has: 0
added '7'. Count=1
added '2'. Count=1
added '5'. Count=1
buffer time elapsed, current queue contents is: 3 items.
item 7 at 25/01/2015 22:25:39 +00:00
item 2 at 25/01/2015 22:25:40 +00:00
item 5 at 25/01/2015 22:25:41 +00:00
holderqueue has: 0
Cancelled
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.