繁体   English   中英

可观察到对不同线程上更改的阻塞收集没有反应

[英]Observable not reacting to blocking collection changed on different thread

我有以下代码:

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() 仅被调用一次 这是为什么?! 我已经花了整整一天的时间。 由于我已经使用我的dump方法订阅了observable,因此它应该“观察”集合的更改并每3秒调用一次DumpToConsole()方法。 那就是我需要的。

有想法吗? 谢谢

(PS传递给SNotifier类的操作是我删除SNotifier中与控制台相关的内容的方式,我需要对其进行更好的重构,因为与问题本身无关,因此可以忽略它)

您正在BlockingCollection<int>上调用ToObservable() 此扩展方法仅采用集合上的IEnumerable<int>接口并将其转换为IObservable<int> 这具有在订阅时获取集合内容列表并通过Observable流将其转储的效果。

添加项目时,它将不会继续枚举项目。

GetConsumingEnumerable()前面使用ToObservable()将解决此问题。

但是,需要谨慎,因为这也会从集合中删除项目,这可能是不希望的。

如果可以接受,则在多个订阅者的情况下,您可能希望发布可观察到的结果,以避免造成严重破坏。

如果您只是添加内容,则可以考虑扭转整个局面-使用Subject返回“ Add”方法,并让一个订阅者填写列表(如果需要,则填充BlockingCollection)以跟踪集合,而第二个订阅者可以然后报告进度。

另一种方法是使用ObservableCollection并订阅其事件。

在最后两个建议中,由于Subject<T>ObservableCollection<T>本身都不是线程安全的,因此您需要使“ Add”成为线程安全的。

附录

布兰登关于您要在StartQueue中处置订阅的StartQueue使我意识到另一个问题StartQueue将永远不会返回! 这是因为在枚举完成之前,对IEnumerableToObservable()转换进行的对Subscribe的调用将不会返回-因此它也保留了处置(因为IDisposableSubscribe的返回值),这就是为什么我没有注意using @Brandon指出的任何一个!

基于以上两点,您需要进行以下附加更改。 首先,删除订阅周围的using语句,隐式处置将取消订阅。 当我们解决阻塞的Subscribe呼叫时,这将导致立即取消订阅。 如果确实需要在某个时候显式退订,则应保留IDisposable句柄。

其次,在ToObservable()之后立即添加对SubscribeOn(Scheduler.Default)的调用,以防止Subscribe呼叫阻塞。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM