Frage

Gibt es eine schöne einfache Methode einen Funktionsaufruf der Verzögerung während lassen den Faden weiter ausführen?

z.

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
}

Ich bin mir bewusst, dass dies unter Verwendung einer Timer und Ereignisprozeduren erreicht werden kann, aber ich frage mich, ob es eine Standard c # Weg, dies zu erreichen?

Wenn jemand neugierig ist, dass der Grund, warum dies erforderlich ist, ist, dass foo () und bar () ist in verschiedenen (Singleton) Klassen, die mein Bedürfnis, sie unter außergewöhnlichen Umständen zu nennen. Das Problem ist, dass dies bei der Initialisierung erfolgt so foo bar aufrufen muss, die eine Instanz der Klasse foo benötigt, die erstellt wird ... daher zu versperren den verzögerten call (), dass foo um sicherzustellen, dass diese wieder vollständig instanziiert .. Lesen schmatzt fast schlechtes Design!

EDIT

Ich werde die Punkte über schlechtes Design unter advisement nehmen! Ich habe lange gedacht, dass ich das System zu verbessern, aber vielleicht in der Lage sein, diese unangenehme Situation nur tritt auf, wenn eine Ausnahme ausgelöst wird, zu allen anderen Zeiten die beiden Singletons sehr schön koexistieren. Ich glaube, dass ich nicht mit fiesen Asynchron-prasselt auf messaround gehen, sondern ich werde die Initialisierung einer der Klassen Refactoring.

War es hilfreich?

Lösung

Dank moderner C # 5/6:)

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

public void bar()
{
    // do stuff
}

Andere Tipps

Ich habe für so etwas wie diese selbst suchen - ich kam mit dem folgenden, obwohl es einen Timer nicht verwendet, verwendet er es nur einmal für die erste Verzögerung und erfordert keine Sleep Anrufe ...

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
}

(dank Fred Deschenes für die Idee des Timers innerhalb der Callback-Entsorgung)

Neben dem Design Beobachtungen des früheren commenters, keine der Lösungen zu vereinbaren waren sauber genug für mich. .NET 4 liefert Dispatcher und Task Klassen, die Verzögerung der Ausführung machen für den aktuellen Thread ist ziemlich einfach:

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

Für Kontext, verwende ich diese eine ICommand zum Entprellen zu einer linken Maustaste oben auf einem UI-Elemente gebunden. Benutzer sind Doppelklick, die alle Arten von Chaos verursachen. (Ich weiß, dass ich auch Click / DoubleClick Handler verwenden könnte, aber ich wollte eine Lösung, die mit ICommands auf der ganzen Linie arbeitet).

public void Execute(object parameter)
{
    if (!IsDebouncing) {
        IsDebouncing = true;
        AsyncUtils.DelayCall (DebouncePeriodMsec, () => {
            IsDebouncing = false;
        });

        _execute ();
    }
}

Es klingt wie die Kontrolle über die Schaffung beide dieser Objekte und ihre gegenseitige Abhängigkeit muss extern gesteuert, anstatt zwischen den Klassen selbst.

Es ist in der Tat ein sehr schlechtes Design, allein Singletons lassen sich allein schlechtes Design.

Wenn Sie jedoch wirklich Ausführung brauchen zu verzögern, ist hier, was Sie tun können:

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

Dies wird jedoch, rufen bar() auf einem separaten Thread. Wenn Sie bar() im ursprünglichen Thread aufrufen müssen Sie müssen möglicherweise bar() Aufruf bewegen Handler RunWorkerCompleted oder ein bisschen Hacking mit SynchronizationContext tun.

Nun, würde ich mit dem „Design“ Punkt einig ... aber man kann wahrscheinlich einen Monitor verwenden, man wissen zu lassen, wenn der andere hinter dem kritischen Abschnitt ist ...

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

        }


    }

}

Verbrauch:

DelayedDelegate.Add(MyMethod,5);

void MyMethod()
{
     MessageBox.Show("5 Seconds Later!");
}

ich aber die perfekte Lösung wäre ein Timer zu haben, die mit verzögerter Wirkung zu behandeln. FxCop mag es nicht, wenn Sie ein Intervall haben weniger als eine Sekunde. Ich muss meine Aktionen verzögern, bis nach meinem Datagrid durch Spalte abgeschlossen Sortierung. Ich dachte, ein One-Shot-Timer (Autoreset = false) wäre die Lösung, und es funktioniert perfekt. AND, FxCop wird mir die Warnung nicht zulassen, unterdrücken!

Dies wird entweder auf ältere Versionen von .NET
Cons arbeitet: wird in einem eigenen Thread ausführen

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

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

        private CancelableDelay() { }

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

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

    }

Verbrauch:

var job = CancelableDelay.StartAfter(1000, () => { WorkAfter1sec(); });  
job.Cancel(); //to cancel the delayed job

Es gibt keine Standardmethode einen Aufruf eine andere Funktion zu verzögern als einen Timer und Ereignisse zu verwenden.

Das klingt nach den GUI anti Mustern einen Anruf auf ein Verfahren zu verzögern, so dass Sie das Formular fertig ist sicher sein können, Auslegen. Keine gute Idee.

Aufbauend auf die Antwort von David O'Donoghue hier ist eine optimierte Version des verzögerten Delegierten:

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

Die Klasse etwas mehr sein könnte durch die Verwendung eines eindeutigen Schlüssel für die Delegierten verbessert. Denn wenn man die gleichen Delegierten ein zweites Mal hinzufügen, bevor die erste gefeuert, könnten Sie ein Problem mit dem Wörterbuch bekommen.

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));
}
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top