C# event debounce

后端 未结 14 1084
南旧
南旧 2020-11-28 06:22

I\'m listening to a hardware event message, but I need to debounce it to avoid too many queries.

This is an hardware event that sends the machine status and I have t

14条回答
  •  -上瘾入骨i
    2020-11-28 06:59

    Recently I was doing some maintenance on an application that was targeting an older version of the .NET framework (v3.5).

    I couldn't use Reactive Extensions nor Task Parallel Library, but I needed a nice, clean, consistent way of debouncing events. Here's what I came up with:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Threading;
    
    namespace MyApplication
    {
        public class Debouncer : IDisposable
        {
            readonly TimeSpan _ts;
            readonly Action _action;
            readonly HashSet _resets = new HashSet();
            readonly object _mutex = new object();
    
            public Debouncer(TimeSpan timespan, Action action)
            {
                _ts = timespan;
                _action = action;
            }
    
            public void Invoke()
            {
                var thisReset = new ManualResetEvent(false);
    
                lock (_mutex)
                {
                    while (_resets.Count > 0)
                    {
                        var otherReset = _resets.First();
                        _resets.Remove(otherReset);
                        otherReset.Set();
                    }
    
                    _resets.Add(thisReset);
                }
    
                ThreadPool.QueueUserWorkItem(_ =>
                {
                    try
                    {
                        if (!thisReset.WaitOne(_ts))
                        {
                            _action();
                        }
                    }
                    finally
                    {
                        lock (_mutex)
                        {
                            using (thisReset)
                                _resets.Remove(thisReset);
                        }
                    }
                });
            }
    
            public void Dispose()
            {
                lock (_mutex)
                {
                    while (_resets.Count > 0)
                    {
                        var reset = _resets.First();
                        _resets.Remove(reset);
                        reset.Set();
                    }
                }
            }
        }
    }
    

    Here's an example of using it in a windows form that has a search text box:

    public partial class Example : Form 
    {
        private readonly Debouncer _searchDebouncer;
    
        public Example()
        {
            InitializeComponent();
            _searchDebouncer = new Debouncer(TimeSpan.FromSeconds(.75), Search);
            txtSearchText.TextChanged += txtSearchText_TextChanged;
        }
    
        private void txtSearchText_TextChanged(object sender, EventArgs e)
        {
            _searchDebouncer.Invoke();
        }
    
        private void Search()
        {
            if (InvokeRequired)
            {
                Invoke((Action)Search);
                return;
            }
    
            if (!string.IsNullOrEmpty(txtSearchText.Text))
            {
                // Search here
            }
        }
    }
    

提交回复
热议问题