سؤال

هل هناك طريقة بسيطة لطيفة لتأخير مكالمة وظيفة أثناء ترك الخيط متابعة التنفيذ؟

على سبيل المثال

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 # لتحقيق ذلك؟

إذا كان أي شخص فضولي، فإن السبب في أن هذا مطلوب هو أن فو () وبار () في فصول مختلفة (Singleton) التي حاجتي في الاتصال ببعضها البعض في ظروف استثنائية. المشكلة في أن يتم ذلك في التهوية، لذلك تحتاج FOO إلى استدعاء الشريط الذي يحتاج إلى مثيل لفئة FOO التي يتم إنشاؤها ... وبالتالي الاتصال المتأخر للبار () للتأكد من أن FOO مثبتة تماما .. قراءة هذا الظهر تقريبا صفعات التصميم السيئ!

تعديل

سوف آخذ النقاط حول التصميم السيئ في ظل assisement! لقد اعتقدت لفترة طويلة أنني قد أكون قادرا على تحسين النظام، ومع ذلك، فإن هذا الوضع السيء فقط يحدث عندما يتم طرح استثناء، في جميع الأوقات الأخرى، التعايش المفردان بشكل جيد للغاية. أعتقد أنني لن Messaround مع طابقات ASYNC سيئة، بدلا من ذلك، سأقوم بإعادة تكوين إبعاد أحد الفصول.

هل كانت مفيدة؟

المحلول

شكرا للحديث C # 5/6 :)

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

public void bar()
{
    // do stuff
}

نصائح أخرى

لقد كنت أبحث عن شيء مثل هذا بنفسي - لقد توصلت إلى ما يلي، على الرغم من أنه يستخدم مؤقتا، فإنه يستخدمه مرة واحدة فقط للتأخير الأولي، ولا يتطلب أي Sleep المكالمات ...

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
}

(شكرا ل فريد ديزنيز لفكرة التخلص من الموقت داخل رد الاتصال)

بصرف النظر عن الموافقة على الملاحظات التصميمية للمعلقين السابقين، كانت أي من الحلول نظيفة بما يكفي بالنسبة لي. .NET 4 يوفر Dispatcher و Task الفصول التي تجعل التنفيذ تأخير على الموضوع الحالي بسيطة جدا:

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

للسياق، أنا أستخدم هذا ليعينك ICommand مرتبطة بأزرار الماوس الأيسر على عنصر واجهة المستخدم. يتعين على المستخدمين النقر المزدوج الذي كان يسبب جميع أنواع الفوضى. (أعرف أنني يمكن أن تستخدم أيضا Click/DoubleClick معالجات، لكنني أردت حلا يعمل معه ICommandق عبر مجلس الإدارة).

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

        _execute ();
    }
}

يبدو أن التحكم في إنشاء كل من هذه الكائنات ويحتاج الترابط إلى السيطرة خارجيا، وليس بين الطبقات نفسها.

إنه بالفعل تصميم سيء للغاية، ناهيك عن Singleton من خلال حد ذاته تصميم سيئ.

ومع ذلك، إذا كنت بحاجة فعلا إلى تأخير الإعدام، فإليك ما قد تفعله:

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

هذا سوف يستحدع bar() على موضوع منفصل. إذا كنت بحاجة إلى الاتصال bar() في الخيط الأصلي قد تحتاج إلى التحرك bar() الاحتجاج RunWorkerCompleted معالج أو تفعل القليل من القرصنة SynchronizationContext.

حسنا، يجب أن أتفق مع نقطة "التصميم" ... ولكن من المحتمل أن تستخدم مراقبا لترك المرء يعرف متى تجاوز الآخر القسم الحرج ...

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

        }


    }

}

الاستعمال:

DelayedDelegate.Add(MyMethod,5);

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

أنا على الرغم من أن الحل الأمثل سيكون لديه مقبض مؤقت الإجراء المتأخر. لا يحب FXCOP عندما يكون لديك فاصل أقل ثم ثانية واحدة. أحتاج إلى تأخير أفعالي حتى بعد اكتمال DataGrid الخاص بي عن طريق العمود. احسب جهاز توقيت واحد (Autoreset = FALSE) سيكون الحل، ويعمل تماما. و، لن يسمح لي FXCOP بقمع التحذير!

هذا سيعمل إما على الإصدارات القديمة من .NET
سلبيات: سوف تنفذ في موضوعه الخاص

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

    }

الاستعمال:

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

لا توجد طريقة قياسية لتأخير مكالمة إلى وظيفة أخرى غير استخدام مؤقت وأحداث.

هذا يبدو وكأنه نمط واجهة المستخدم الرسومية لمكافحة تأخير المكالمة إلى طريقة حتى تتمكن من التأكد من أنه انتهى النموذج. ليست فكرة جيدة.

بناء على إجابة David O'Donoghue إليك نسخة محسنة من المندوب المتأخر:

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

يمكن تحسين الفصل بشكل طفيف باستخدام مفتاح فريد للمندوبين. لأنه إذا قمت بإضافة نفس المفوض مرة ثانية قبل إطلاق أول واحد، فقد تحصل على مشكلة في القاموس.

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));
}
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top