简体   繁体   English

如何实现排序缓冲区?

[英]How to implement a sorted buffer?

I need to traverse a collection of disjoint folders;我需要遍历一组不相交的文件夹; each folder is associated to a visited time configurated somewhere in the folder.每个文件夹都与文件夹中某处配置的访问时间相关联。

I then sort the folders, and process the one with the earliest visited time first.然后我对文件夹进行排序,首先处理访问时间最早的文件夹。 Note the processing is generally slower than the traversing.请注意,处理通常比遍历慢。

My code targets Framework4.8.1;我的代码针对Framework4.8.1; Currently my implementation is as follows:目前我的实现如下:

    public class BySeparateThread
    {
        ConcurrentDictionary<string, DateTime?> _dict = new ConcurrentDictionary<string, DateTime?>();
        private object _lock;

        /// <summary>
        /// this will be called by producer thread;
        /// </summary>
        /// <param name="address"></param>
        /// <param name="time"></param>
        public void add(string address,DateTime? time) {
            _dict.TryAdd(address, time);
            
        }

        /// <summary>
        /// called by subscriber thread;
        /// </summary>
        /// <returns></returns>
        public string? next() {
            lock (_lock) {
                var r = _dict.FirstOrDefault();
                
                //return sortedList.FirstOrDefault().Value;


                if (r.Key is null)
                {
                    return r.Key;
                }

                if (r.Value is null)
                {
                    _dict.TryRemove(r.Key, out var _);
                    return r.Key;
                }

                var key = r.Key;

                foreach (var item in _dict.Skip(1) )
                {
                    if (item.Value is null)
                    {
                        _dict.TryRemove(item.Key, out var _);
                        return item.Key;
                    }
                    if (item.Value< r.Value)
                    {
                        r=item;
                    }
                }
                _dict.TryRemove(key, out var _);

                return key;
            }
        }

        /// <summary>
        /// this will be assigned of false by producer thread;
        /// </summary>
        public bool _notComplete = true;

        /// <summary>
        /// shared configuration for subscribers;
        /// </summary>
        fs.addresses_.disjoint.deV_._bak.Io io; //.io_._CfgX.Create(cancel, git)

        /// <summary>
        /// run this in a separate thread other than <see cref="add(string, DateTime?)"/>
        /// </summary>
        /// <param name="sln"></param>
        /// <returns></returns>
        public async Task _asyn_ofAddress(string sln)
        {
            while (_notComplete)
            {
                var f = next();
                if (f is null )
                {
                    await Task.Delay(30*1000);
                    //await Task.Yield();
                    continue;
                }
                /// degree of concurrency is controlled by a semophore; for instance, at most 4 are tackled:
                new dev.srcs.each.sln_.delvable.Bak_srcsInAddresses(io)._startTask_ofAddress(sln);
            }
        }
    }

For the above, I'm concerned about the while(_notComplete) part, as it looks like there would be many loops doing nothing there.对于上面的内容,我担心while(_notComplete)部分,因为看起来会有很多循环在那里什么都不做。 I think there should be better ways to remove the while by utilizing the fact that the collection can notify whether it's empty or not at some/various stages such as when we add .我认为应该有更好的方法来删除while通过利用集合可以在某些/不同阶段通知它是否为空这一事实,例如当我们add时。

There would be better implementation which can be based on some mature framework such as those being considered by me these days but I often stopped wondering at some implementation details:会有更好的实现,它可以基于一些成熟的框架,比如这些天我正在考虑的框架,但我经常不再想知道一些实现细节:

  1. BlockingCollection for this one, I don't know how to make the collection added and sorted dynamically while producer and subscriber are on the run; BlockingCollection 对于这个,我不知道如何在生产者和订阅者运行时动态添加和排序集合;
  2. Channel Again, I could not come up with one fitting my need after I read its examples; Channel 同样,在阅读其示例后,我无法提出适合我需要的频道;
  3. Pipeline I havenot fully understood it;管道我还没有完全理解它;
  4. Rx I tried to implement an observable and an observer. Rx 我尝试实现一个可观察对象和一个观察者。 It only gives me a macroscope framework, but when I get into the details, I ended with what I'm currently doing and I begin to wonder: with what I'm doing, I don't need Rx here.它只给了我一个宏观框架,但当我深入细节时,我以我目前正在做的事情结束,我开始怀疑:我正在做的事情,我在这里不需要 Rx。
  5. Dataflow Shall I implement my own BufferBlock or ActionBlock? Dataflow 我应该实现自己的 BufferBlock 还是 ActionBlock? It seems the built-in bufferBlock cannot be customized to sort things before releasing them to the next block.似乎无法自定义内置 bufferBlock 以在将它们释放到下一个块之前对它们进行排序。

Sorting buffered Observables seems similar to my problem; 对缓冲的 Observables 进行排序似乎与我的问题相似; but it ends with a solution similar to the one I currently have but am not satisfied with, as stated in the above.但它以类似于我目前拥有但不满意的解决方案结束,如上文所述。

Could some one give me a sample code?有人可以给我一个示例代码吗? Please give as concrete code as you can;请尽可能给出具体的代码; As you can see, I have researched some general ideas/paths and finally what stops me short is the details, which are often glossed over in some docs.如您所见,我研究了一些一般性的想法/路径,最后让我失望的是细节,这些细节经常在一些文档中被掩盖。

I just found one solution which is better than my current one.我刚刚找到了一种比我目前的解决方案更好的解决方案。 I believe there are some even better ones, so please do post your answers if you find some;我相信还有一些更好的,所以如果你找到了,请张贴你的答案; my current one is just what I can hack for what I know so far.我目前的版本正是我目前所知道的可以破解的。

I found Prioritized queues in Task Parallel Library , and I write a similar one for my case:在 Task Parallel Library 中找到了 Prioritized queues ,我为我的案例写了一个类似的队列:

using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reactive.Subjects;
using System.Threading;
using System.Threading.Tasks;

namespace nilnul.dev.srcs.every.slns._bak
{

    public class BySortedSet : IProducerConsumerCollection<(string, DateTime)>
    {
        private class _Comparer : IComparer<(string, DateTime)>
        {
            public int Compare((string, DateTime) first, (string, DateTime) second)
            {
                var returnValue = first.Item2.CompareTo(second.Item2);
                if (returnValue == 0)
                    returnValue = first.Item1.CompareTo(second.Item1);
                return returnValue;
            }
            static public _Comparer Singleton
            {
                get
                {
                    return nilnul._obj.typ_.nilable_.unprimable_.Singleton<_Comparer>.Instance;// just some magic to get an instance
                }
            }
        }

        SortedSet<(string, DateTime)> _dict = new SortedSet<(string, DateTime)>(
            _Comparer.Singleton
        );

        private object _lock=new object();

        public int Count
        {
            get
            {
                lock(_lock){
                    return _dict.Count;
                }
            }
        }

        public object SyncRoot => _lock;

        public bool IsSynchronized => true;

        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
            //throw new NotImplementedException();
        }

        public void CopyTo((string, DateTime)[] array, int index)
        {
            lock (_lock)
            {
                foreach (var item in _dict)
                {
                    array[index++] = item;
                }
            }

        }
        public void CopyTo(Array array, int index)
        {
            lock (_lock)
            {
                foreach (var item in _dict)
                {
                    array.SetValue(item, index++);
                }
            }

        }

        public bool TryAdd((string, DateTime) item)
        {
            lock (_lock)
            {
                return _dict.Add(item);
            }
        }

        public bool TryTake(out (string, DateTime) item)
        {
            lock (_lock)
            {
                item = _dict.Min;
                if (item==default)
                {
                    return false;
                }
                return _dict.Remove(item);
            }
        }

        public (string, DateTime)[] ToArray()
        {
            lock (_lock)
            {
                return this._dict.ToArray();

            }
        }

        public IEnumerator<(string, DateTime)> GetEnumerator()
        {
            return ToArray().AsEnumerable().GetEnumerator();
        }

        /// <summary>
        /// </summary>
        /// <returns></returns>
        public BlockingCollection<(string, DateTime)> asBlockingCollection() {
            return new BlockingCollection<(string, DateTime)>(
                this
            );
        }
    }
}

Then I can use that like:然后我可以像这样使用它:

        static public void ExampleUse(CancellationToken cancellationToken) {
            var s = new BySortedSet().asBlockingCollection();

            /// traversal thread:
            s.Add(("", DateTime.MinValue));

            //...
            s.CompleteAdding();

            /// tackler thread:
            ///
            foreach (var item in s.GetConsumingEnumerable(cancellationToken))
            {
                ///  process the item;
                /// todo: degree of parallelism is controlled by the tackler, or is there a better way like in dataflow or Rx or sth else?
            }
        }

Thanks!谢谢!

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

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