C# .NET - Buffer messages w/Timer

后端 未结 2 1015
佛祖请我去吃肉
佛祖请我去吃肉 2021-01-24 19:22

I need to implement a message buffering system that is also timed based.

What I need to do is store instances of my class and then send them forward either when I reach

相关标签:
2条回答
  • 2021-01-24 19:36

    There is a fantastic library for these kind of requirements (combine time with sequences), it is Reactive Extensions. See https://github.com/Reactive-Extensions/Rx.NET

    You could then write something like

    void Main()
    {
        messages
            .Buffer(TimeSpan.FromMinutes(1), 100) // Buffer until 100 items or 1 minute has elapsed, whatever comes first.
            .Subscribe(msgs => SendMessages(msgs));     
    }
    
    Subject<Message> messages = new Subject<Message>();
    
    public void GotNewMessage(Message msg)
    {
        messages.OnNext(msg);
    }
    

    Note: this is not production ready but it shows the basic of how to do it. Depending on where you het the messages from there are better ways to create an Observable to subscribe to.

    More references:

    • http://www.introtorx.com/
    • https://msdn.microsoft.com/en-us/library/hh242985(v=vs.103).aspx

    If your message are received using an event you can link the event to a RX stream, see https://msdn.microsoft.com/en-us/library/hh242978(v=vs.103).aspx and https://msdn.microsoft.com/en-us/library/system.reactive.linq.observable.fromeventpattern(v=vs.103).aspx

    0 讨论(0)
  • 2021-01-24 19:50

    First of all, you should consider using a ConcurrentQueue<> insted of a List<>. ConcurrentQueue<> is all the way thread safe and needs no additional locks. With this, you have already spared yourself a lock for the message queue. Interlocked provides atomicity, when it's not available.

    According to the C# language specification, independent reads/writes are atomic (but only for some data types and long is not always atomic - that's why I shifted the DateTime.Now.Ticks to get an int32 without losing any bits that would influence the elapsed time) and read-modify-write (eg. ++i) is never atomic.

    Shifting (eg. <<) is on its own atomic and doesn't need any additional locking.

    private ConcurrentQueue<Message> Queue = new ConcurrentQueue<Message>();
    private int QueueSize = 0;
    private int LastSend = (int)(DateTime.Now.Ticks >> 23);
    private int LastMessage = (int)(DateTime.Now.Ticks >> 23);
    
    public void GotNewMessage(Message Message)
    {
        Queue.Enqueue(Message);
    
        Interlocked.Increment(ref QueueSize);
        Interlocked.Exchange(ref LastMessage, (int)(DateTime.Now.Ticks >> 23));
    
        if (Interlocked.CompareExchange(ref QueueSize, 0, 100) >= 100 || 
            LastMessage - LastSend >= 60)
        {
            Message Dummy;
            while (!Queue.IsEmpty)
                if (Queue.TryDequeue(out Dummy))
                    SendMessage(Dummy);
    
            Interlocked.Exchange(ref LastSend, (int)(DateTime.Now.Ticks >> 23));
        }
    }
    
    public void SendMessage(Message Message)
    {
        // ...
    }
    

    Edit: It may occur, that more than 100 messages are sent out. If you wish to send out strictly 100 messages, you can implement an another atomic incrementation in the cycle.

    0 讨论(0)
提交回复
热议问题