Delayed function calls

后端 未结 12 1270
星月不相逢
星月不相逢 2020-11-30 22:07

Is there a nice simple method of delaying a function call whilst letting the thread continue executing?

e.g.

public void foo()
{
    // Do stuff!

           


        
相关标签:
12条回答
  • 2020-11-30 22:16

    Well, I'd have to agree with the "design" point... but you can probably use a Monitor to let one know when the other is past the critical section...

        public void foo() {
            // Do stuff!
    
            object syncLock = new object();
            lock (syncLock) {
                // Delayed call to bar() after x number of ms
                ThreadPool.QueueUserWorkItem(delegate {
                    lock(syncLock) {
                        bar();
                    }
                });
    
                // Do more Stuff
            } 
            // lock now released, bar can begin            
        }
    
    0 讨论(0)
  • 2020-11-30 22:21

    I've been looking for something like this myself - I came up with the following, although it does use a timer, it uses it only once for the initial delay, and doesn't require any Sleep calls ...

    public void foo()
    {
        System.Threading.Timer timer = null; 
        timer = new System.Threading.Timer((obj) =>
                        {
                            bar();
                            timer.Dispose();
                        }, 
                    null, 1000, System.Threading.Timeout.Infinite);
    }
    
    public void bar()
    {
        // do stuff
    }
    

    (thanks to Fred Deschenes for the idea of disposing the timer within the callback)

    0 讨论(0)
  • 2020-11-30 22:22
    private static volatile List<System.Threading.Timer> _timers = new List<System.Threading.Timer>();
            private static object lockobj = new object();
            public static void SetTimeout(Action action, int delayInMilliseconds)
            {
                System.Threading.Timer timer = null;
                var cb = new System.Threading.TimerCallback((state) =>
                {
                    lock (lockobj)
                        _timers.Remove(timer);
                    timer.Dispose();
                    action()
                });
                lock (lockobj)
                    _timers.Add(timer = new System.Threading.Timer(cb, null, delayInMilliseconds, System.Threading.Timeout.Infinite));
    }
    
    0 讨论(0)
  • 2020-11-30 22:27

    I though the perfect solution would be to have a timer handle the delayed action. FxCop doesn't like when you have an interval less then one second. I need to delay my actions until AFTER my DataGrid has completed sorting by column. I figured a one-shot timer (AutoReset = false) would be the solution, and it works perfectly. AND, FxCop will not let me suppress the warning!

    0 讨论(0)
  • 2020-11-30 22:28

    Aside from agreeing with the design observations of the previous commenters, none of the solutions were clean enough for me. .Net 4 provides Dispatcher and Task classes which make delaying execution on the current thread pretty simple:

    static class AsyncUtils
    {
        static public void DelayCall(int msec, Action fn)
        {
            // Grab the dispatcher from the current executing thread
            Dispatcher d = Dispatcher.CurrentDispatcher;
    
            // Tasks execute in a thread pool thread
            new Task (() => {
                System.Threading.Thread.Sleep (msec);   // delay
    
                // use the dispatcher to asynchronously invoke the action 
                // back on the original thread
                d.BeginInvoke (fn);                     
            }).Start ();
        }
    }
    

    For context, I'm using this to debounce an ICommand tied to a left mouse button up on a UI element. Users are double clicking which was causing all kinds of havoc. (I know I could also use Click/DoubleClick handlers, but I wanted a solution that works with ICommands across the board).

    public void Execute(object parameter)
    {
        if (!IsDebouncing) {
            IsDebouncing = true;
            AsyncUtils.DelayCall (DebouncePeriodMsec, () => {
                IsDebouncing = false;
            });
    
            _execute ();
        }
    }
    
    0 讨论(0)
  • 2020-11-30 22:29

    Building upon the answer from David O'Donoghue here is an optimized version of the Delayed Delegate:

    using System.Windows.Forms;
    using System.Collections.Generic;
    using System;
    
    namespace MyTool
    {
        public class DelayedDelegate
        {
           static private DelayedDelegate _instance = null;
    
            private Timer _runDelegates = null;
    
            private Dictionary<MethodInvoker, DateTime> _delayedDelegates = new Dictionary<MethodInvoker, DateTime>();
    
            public DelayedDelegate()
            {
            }
    
            static private DelayedDelegate Instance
            {
                get
                {
                    if (_instance == null)
                    {
                        _instance = new DelayedDelegate();
                    }
    
                    return _instance;
                }
            }
    
            public static void Add(MethodInvoker pMethod, int pDelay)
            {
                Instance.AddNewDelegate(pMethod, pDelay * 1000);
            }
    
            public static void AddMilliseconds(MethodInvoker pMethod, int pDelay)
            {
                Instance.AddNewDelegate(pMethod, pDelay);
            }
    
            private void AddNewDelegate(MethodInvoker pMethod, int pDelay)
            {
                if (_runDelegates == null)
                {
                    _runDelegates = new Timer();
                    _runDelegates.Tick += RunDelegates;
                }
                else
                {
                    _runDelegates.Stop();
                }
    
                _delayedDelegates.Add(pMethod, DateTime.Now + TimeSpan.FromMilliseconds(pDelay));
    
                StartTimer();
            }
    
            private void StartTimer()
            {
                if (_delayedDelegates.Count > 0)
                {
                    int delay = FindSoonestDelay();
                    if (delay == 0)
                    {
                        RunDelegates();
                    }
                    else
                    {
                        _runDelegates.Interval = delay;
                        _runDelegates.Start();
                    }
                }
            }
    
            private int FindSoonestDelay()
            {
                int soonest = int.MaxValue;
                TimeSpan remaining;
    
                foreach (MethodInvoker invoker in _delayedDelegates.Keys)
                {
                    remaining = _delayedDelegates[invoker] - DateTime.Now;
                    soonest = Math.Max(0, Math.Min(soonest, (int)remaining.TotalMilliseconds));
                }
    
                return soonest;
            }
    
            private void RunDelegates(object pSender = null, EventArgs pE = null)
            {
                try
                {
                    _runDelegates.Stop();
    
                    List<MethodInvoker> removeDelegates = new List<MethodInvoker>();
    
                    foreach (MethodInvoker method in _delayedDelegates.Keys)
                    {
                        if (DateTime.Now >= _delayedDelegates[method])
                        {
                            method();
    
                            removeDelegates.Add(method);
                        }
                    }
    
                    foreach (MethodInvoker method in removeDelegates)
                    {
                        _delayedDelegates.Remove(method);
                    }
                }
                catch (Exception ex)
                {
                }
                finally
                {
                    StartTimer();
                }
            }
        }
    }
    

    The class could be slightly more improved by using a unique key for the delegates. Because if you add the same delegate a second time before the first one fired, you might get a problem with the dictionary.

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