Задержанные вызовы функций - PullRequest
       24

Задержанные вызовы функций

67 голосов
/ 13 февраля 2009

Есть ли хороший простой метод задержки вызова функции, позволяя потоку продолжить выполнение?

, например

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
}

Я знаю, что этого можно добиться с помощью таймера и обработчиков событий, но мне было интересно, существует ли стандартный способ c # для этого?

Если кому-то интересно, причина этого в том, что foo () и bar () находятся в разных (одноэлементных) классах, которые мне нужно вызывать друг для друга в исключительных обстоятельствах. Проблема заключается в том, что это делается при инициализации, поэтому foo нужно вызвать bar, которому нужен экземпляр класса foo, который создается ... отсюда отложенный вызов bar (), чтобы убедиться, что foo полностью создан. почти попахивает плохим дизайном!

EDIT

Я расскажу о плохом дизайне! Я долго думал, что смогу улучшить систему, однако, эта неприятная ситуация только возникает, когда выдается исключение, во всех других случаях два синглета очень хорошо сосуществуют. Я думаю, что я не собираюсь связываться с неприятными асинхронными шаблонами, скорее, я собираюсь реорганизовать инициализацию одного из классов.

Ответы [ 12 ]

0 голосов
/ 27 сентября 2013

Опираясь на ответ Дэвида О'Донохью, здесь приведена оптимизированная версия задержанного делегата:

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

Класс можно немного улучшить, если использовать уникальный ключ для делегатов. Потому что, если вы добавите один и тот же делегат во второй раз до запуска первого, у вас может возникнуть проблема со словарем.

0 голосов
/ 13 февраля 2009

Не существует стандартного способа отложить вызов функции, кроме использования таймера и событий.

Это звучит как анти-графический шаблон задержки вызова метода, так что вы можете быть уверены, что форма закончила выкладку. Не очень хорошая идея.

...