116

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

e.g.

public void foo()
{
    // Do stuff!

    // Delayed call to bar() after x number of ms

    // Do more Stuff
}

public void bar()
{
    // Only execute once foo has finished
}

I'm aware that this can be achieved by using a timer and event handlers, but I was wondering if there is a standard c# way to achieve this?

If anyone is curious, the reason that this is required is that foo() and bar() are in different (singleton) classes which my need to call each other in exceptional circumstances. The problem being that this is done at initialisation so foo needs to call bar which needs an instance of the foo class which is being created... hence the delayed call to bar() to ensure that foo is fully instanciated.. Reading this back almost smacks of bad design !

EDIT

I'll take the points about bad design under advisement! I've long thought that I might be able to improve the system, however, this nasty situation only occurs when an exception is thrown, at all other times the two singletons co-exist very nicely. I think that I'm not going to messaround with nasty async-patters, rather I'm going to refactor the initialisation of one of the classes.

ROMANIA_engineer
  • 54,432
  • 29
  • 203
  • 199
TK.
  • 46,577
  • 46
  • 119
  • 147
  • You need to fix it but not by using threads (or any other asyn practice for that matter) – ShuggyCoUk Feb 13 '09 at 11:00
  • 1
    Having to use threads to synchronize object initialization is the sign that you should take another way. Orchestrator seems a better option. – thinkbeforecoding Feb 13 '09 at 11:06
  • 1
    Resurrection! -- Commenting on the design, you could make a choice to have a two-stage initialization. Drawing from the Unity3D API, there are `Awake` and `Start` phases. In the `Awake` phase, you configure yourself, and by the end of this phase all objects are initialized. During the `Start` phase the objects can begin communicating with each other. – cod3monk3y Mar 05 '14 at 17:08
  • 1
    The accepted answer needs to be changed – Brian Webster Aug 09 '18 at 13:54

12 Answers12

266

Thanks to modern C# 5/6 :)

public void foo()
{
    Task.Delay(1000).ContinueWith(t=> bar());
}

public void bar()
{
    // do stuff
}
Korayem
  • 12,108
  • 5
  • 69
  • 56
  • 21
    This answer is awesome for 2 reasons. Code simplicity and the fact that Delay do NOT create a thread nor use the thread pool like other Task.Run or Task.StartNew... it's internally a timer. – Zyo Sep 02 '16 at 00:31
  • A decent solution. – x4h1d Oct 06 '16 at 08:33
  • 11
    Also note a slightly cleaner (IMO) equivalent version: Task.Delay(TimeSpan.FromSeconds(1)).ContinueWith(_ => bar()); – Taran Nov 19 '17 at 23:04
  • 9
    @Zyo Actually it does use a different thread. Try accessing a UI element from it and it will trigger an exception. – TudorT Mar 16 '18 at 21:28
  • @TudorT - If Zyo is correct that it runs on an already existing thread that runs timer events, then his point is that it does not consume extra resources *creating* a new thread, nor *queuing* to the thread pool. (Though I don't know whether creating a timer is significantly cheaper than queueing a task to thread pool - which ALSO doesn't create a thread, that being the whole point of the thread pool.) – ToolmakerSteve May 19 '18 at 13:39
  • 3
    Here's how you can continue on UI thread: Task.Delay(1000).ContinueWith(_ => { Application.Current.Dispatcher.Invoke(() => { bar(); }); – Eternal21 Mar 10 '20 at 19:43
104

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)

Community
  • 1
  • 1
dodgy_coder
  • 12,407
  • 10
  • 54
  • 67
  • 5
    I feel this is the best answer in general to delay a function call. No thread, no background working, no sleep. Timer are very efficient and memory/cpu wise. – Zyo Sep 27 '12 at 17:08
  • 1
    @Zyo, thanks for your comment - yes timers are efficient, and this sort of delay is useful in many situations, especially when interfacing to something that is out of your control - which doesn't have any support for notification events. – dodgy_coder Sep 28 '12 at 03:17
  • When do you Dispose of the Timer? – Didier A. Oct 25 '12 at 18:48
  • 1
    Reviving an old thread here, but the timer could be disposed like this : public static void CallWithDelay(Action method, int delay) { Timer timer = null; var cb = new TimerCallback((state) => { method(); timer.Dispose(); }); timer = new Timer(cb, null, delay, Timeout.Infinite); } EDIT : Well looks like we can't post code in comments... VisualStudio should format it properly when you copy/paste it anyway :P – Fred Deschenes Sep 17 '13 at 18:04
  • 6
    @dodgy_coder Wrong. Using the `timer` local variable from within the lambda that gets bound to the delegate object `cb` causes it to be hoisted onto an anon store (closure implementation detail) that will cause the `Timer` object to be reachable from the GC's perspective for as long as the `TimerCallback` delegate itself is reachable. In other words, the `Timer` object is **guaranteed** not to be garbage collected until after the delegate object is invoked by the thread pool. – cdhowie Feb 22 '15 at 05:21
  • @cdhowie agreed - I stand corrected. I should have read his comment more carefully. – dodgy_coder Feb 23 '15 at 02:33
  • @cdhowie I've updated the answer to add the disposal of the timer object within the callback, as per FredDeschenes comment. – dodgy_coder Sep 14 '15 at 02:01
  • If the local variable itself remains reachable, then might be safest to put "timer = null;" after the Dispose? Or does the Dispose remove the callback reference that is keeping the variable alive, so not needed? (I realize that once the Dispose is done, the Timer won't be holding any significant resources, so its just a question of whether a few bytes of managed memory keep hanging around.) – ToolmakerSteve Dec 01 '15 at 23:29
  • Still quite useful for Unity user since they haven't upgraded .NET to make Task.Delay() available. – Guntram Apr 28 '17 at 20:38
  • Awesome solution!! Thanks a lot! – Ivan B Feb 03 '20 at 14:57
  • The only thing I would like to add is that this timer IS using threadpool. It could be significant in some cases. https://learn.microsoft.com/en-us/dotnet/api/system.threading.timer?redirectedfrom=MSDN&view=netframework-4.8 – Ivan B Feb 03 '20 at 17:10
17

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 ();
    }
}
cod3monk3y
  • 9,508
  • 6
  • 39
  • 54
7

It sounds like the control of the creation of both these objects and their interdependence needs to controlled externally, rather than between the classes themselves.

Adam Ralph
  • 29,453
  • 4
  • 60
  • 67
7

It's indeed a very bad design, let alone singleton by itself is bad design.

However, if you really do need to delay execution, here's what you may do:

BackgroundWorker barInvoker = new BackgroundWorker();
barInvoker.DoWork += delegate
    {
        Thread.Sleep(TimeSpan.FromSeconds(1));
        bar();
    };
barInvoker.RunWorkerAsync();

This will, however, invoke bar() on a separate thread. If you need to call bar() in the original thread you might need to move bar() invocation to RunWorkerCompleted handler or do a bit of hacking with SynchronizationContext.

John B
  • 20,062
  • 35
  • 120
  • 170
Anton Gogolev
  • 113,561
  • 39
  • 200
  • 288
3

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            
    }
Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
3
public static class DelayedDelegate
{

    static Timer runDelegates;
    static Dictionary<MethodInvoker, DateTime> delayedDelegates = new Dictionary<MethodInvoker, DateTime>();

    static DelayedDelegate()
    {

        runDelegates = new Timer();
        runDelegates.Interval = 250;
        runDelegates.Tick += RunDelegates;
        runDelegates.Enabled = true;

    }

    public static void Add(MethodInvoker method, int delay)
    {

        delayedDelegates.Add(method, DateTime.Now + TimeSpan.FromSeconds(delay));

    }

    static void RunDelegates(object sender, EventArgs e)
    {

        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);

        }


    }

}

Usage:

DelayedDelegate.Add(MyMethod,5);

void MyMethod()
{
     MessageBox.Show("5 Seconds Later!");
}
  • 1
    I would advise to put some logic to avoid the timer to run every 250 milliseconds. First: You can increase the delay to 500 milliseconds because your minimum allowed interval is 1 second. Second: You could start the timer only when new delegates are added, and stop it when there are no more delegates. No reason to keep using CPU cycles when there is nothing to do. Third: you could set the timer interval to the minimum delay across all delegates. So it wakes up only when it needs to invoke a delegate, instead of waking up every 250 milliseconds to see if there is something to do. – Pic Mickael Sep 27 '13 at 12:47
  • MethodInvoker is a Windows.Forms object. Is there an alternative for Web developers please? ie: something that does not clash with System.Web.UI.WebControls. – Fandango68 Nov 21 '16 at 07:51
2

This will work either on older versions of .NET
Cons: will execute in its own thread

class CancellableDelay
    {
        Thread delayTh;
        Action action;
        int ms;

        public static CancellableDelay StartAfter(int milliseconds, Action action)
        {
            CancellableDelay result = new CancellableDelay() { ms = milliseconds };
            result.action = action;
            result.delayTh = new Thread(result.Delay);
            result.delayTh.Start();
            return result;
        }

        private CancellableDelay() { }

        void Delay()
        {
            try
            {
                Thread.Sleep(ms);
                action.Invoke();
            }
            catch (ThreadAbortException)
            { }
        }

        public void Cancel() => delayTh.Abort();

    }

Usage:

var job = CancellableDelay.StartAfter(1000, () => { WorkAfter1sec(); });  
job.Cancel(); //to cancel the delayed job
altair
  • 103
  • 1
  • 6
1

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

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.

Pic Mickael
  • 1,244
  • 19
  • 36
0
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));
}
Koray
  • 1,768
  • 1
  • 27
  • 37
0

There is no standard way to delay a call to a function other than to use a timer and events.

This sounds like the GUI anti pattern of delaying a call to a method so that you can be sure the form has finished laying out. Not a good idea.

ng5000
  • 12,330
  • 10
  • 51
  • 64