我有以下代码:
class Program
{
static void Main(string[] args)
{
var watcher = new SNotifier(DumpToConsole);
watcher.StartQueue();
Console.ReadLine();
}
private static void DumpToConsole(IList<Timestamped<int>> currentCol)
{
Console.WriteLine("buffer time elapsed, current collection contents is: {0} items.", currentCol.Count);
Console.WriteLine("holder has: {0}", currentCol.Count);
}
}
SNotifier:
public class SNotifier
{
private BlockingCollection<int> _holderQueue;
private readonly Action<IList<Timestamped<int>>> _dumpAction;
public SNotifier(Action<IList<Timestamped<int>>> dumpAction)
{
PopulateListWithStartValues();
_dumpAction = dumpAction;
}
public void StartQueue()
{
PopulateQueueOnDiffThread();
var observableCollection = _holderQueue.ToObservable();
var myCollectionTimestamped = observableCollection.Timestamp();
var bufferedTimestampedCollection = myCollectionTimestamped.Buffer(TimeSpan.FromSeconds(3), TimeSpan.FromSeconds(3));
using (bufferedTimestampedCollection.Subscribe(_dumpAction))
{
Console.WriteLine("started observing collection");
}
}
private void PopulateQueueOnDiffThread()
{
Action addToCollectionAction = AddToCollection;
var t = new TaskFactory();
t.StartNew(addToCollectionAction);
}
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;
}
private void AddToCollection()
{
while (true)
{
var newElement = new Random().Next(1, 10);
_holderQueue.Add(newElement);
Console.WriteLine("added {0}", newElement);
Console.WriteLine("holder has: {0}", _holderQueue.Count);
Thread.Sleep(1000);
}
}
private void PopulateListWithStartValues()
{
_holderQueue = new BlockingCollection<int>();
var elements = GetInitialElements();
foreach (var i in elements)
_holderQueue.Add(i);
}
}
我需要运行DumpToConsole()方法来显示每 3秒的集合计数,而此集合的内容在另一个线程上已更改。我的问题是DumpToConsole()只被调用一次。这是为什么?!我已经花了整整一天的时间。由于我已经将我的转储方法订阅到了observable,它应该&#34;观察&#34;集合每隔3秒更改并重新调用DumpToConsole()方法;这就是我需要的。
想法?感谢
(PS传递给SNotifier类的动作是我在SNotifier中删除控制台相关内容的方法,我需要更好地重构,它可以被忽略,因为它与问题本身无关)
答案 0 :(得分:5)
您正在ToObservable()
上致电BlockingCollection<int>
。此扩展方法只需获取集合上的IEnumerable<int>
接口并将其转换为IObservable<int>
。这样可以在订阅处获取集合的内容列表,并通过Observable流将其转储出去。
在添加项目时,它不会继续枚举。
在ToObservable()
前面使用GetConsumingEnumerable()
可解决此问题。
但是,需要注意,因为这也会从集合中删除项目,但这可能是不可取的。
如果这是可以接受的,您可能希望在多个订阅者的情况下发布生成的observable,以避免造成严重破坏。
如果您只是添加,您可以考虑改变整个过程 - 使用Subject来支持“Add”方法并让一个订阅者填充List(或者如果需要,可以使用BlockingCollection)来跟踪集合,以及然后,第二个订户可以报告进度。
另一种方法是使用ObservableCollection并订阅其事件。
在最后两条建议中,您需要使“添加”线程安全,因为Subject<T>
和ObservableCollection<T>
本身都不是线程安全的。
Brandon评论说你在StartQueue
处理订阅让我意识到另一个问题 - StartQueue
永远不会回来!这是因为Subscribe
对ToObservable()
转换IEnumerable
的调用在枚举完成之前不会返回 - 因此它也会暂停(因为IDisposable
是Subscribe
的返回值,这就是为什么我没有注意到using
@Brandon指出的原因!
通过以上两点,您需要进行以下额外更改。首先,删除订阅周围的using
语句,隐式处理将取消订阅。当我们解决阻塞订阅调用时,这将导致订阅立即被取消。如果您确实需要在某些时候明确取消订阅,则应保留IDisposable
句柄。
其次,在SubscribeOn(Scheduler.Default)
之后立即添加对ToObservable()
的来电,以阻止Subscribe
来电阻止。