سؤال

لدي طريقة يجب أن تتأخر بالتشغيل لفترة محددة من الوقت.

هل ينبغي أن أستخدم

Thread thread = new Thread(() => {
    Thread.Sleep(millisecond);
    action();
});
thread.IsBackground = true;
thread.Start();

أو

Timer timer = new Timer(o => action(), null, millisecond, -1);

كنت قرأت بعض مقالات حول استخدام. Thread.Sleep هو تصميم سيء. لكنني لا أفهم حقا السبب.

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

أو إذا كان لديك رموز بديلة للتأخر في التنفيذ، فهي نقدر أيضا.

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

المحلول

فرق واحد هو ذلك System.Threading.Timer يرسل رد الاتصال على موضوع تجمع موضوع، بدلا من إنشاء مؤشر ترابط جديد في كل مرة. إذا كنت بحاجة إلى أن يحدث هذا أكثر من مرة خلال عمر طلبك، فسوف ينقذ هذا النفقات العامة لإنشاء وتدمير مجموعة من الخيوط (عملية كثيفة الموارد للغاية، حيث أن المقال الذي تشير إليه يشير إليه)، لأنه سوف فقط إعادة استخدام المواضيع في المجمع، وإذا كان لديك أكثر من مؤقت واحد يحدث مرة واحدة، فهذا يعني أن لديك عدد أقل من المواضيع التي تعمل في وقت واحد (توفير موارد كبيرة).

بعبارات أخرى، Timer سيكون أكثر كفاءة بكثير. قد تكون أيضا أكثر دقة، منذ Thread.Sleep مضمون فقط للانتظار على الأقل طالما مقدار الوقت الذي تحدده (قد يضعه نظام التشغيل للنوم لفترة أطول بكثير). ممنوح، Timer ما زال لن يكون دقيقا تماما، لكن القصد من ذلك هو إطلاق رد الاتصال على أقرب وقت ممكن من الوقت المحدد، في حين أن هذا ليس بالضرورة نية Thread.Sleep.

أما لتدمير Timer, ، يمكن أن يقبل الاتصال معلمة، لذلك قد تكون قادرة على تمرير Timer نفسها كمعلمة وتخلص من الدعوة في رد الاتصال (على الرغم من أنني لم أحاول ذلك - أعتقد أنه من الممكن أن يكون الموقت مغلقا أثناء رد الاتصال).

تحرير: لا، أعتقد أنك لا تستطيع القيام بذلك، حيث يجب عليك تحديد المعلمة رد الاتصال في Timer منشئ نفسه.

ربما شيء مثل هذا؟ (مرة أخرى، لم تجربها بالفعل)

class TimerState
{
    public Timer Timer;
}

... وبدء الموقت:

TimerState state = new TimerState();

lock (state)
{
    state.Timer = new Timer((callbackState) => {
        action();
        lock (callbackState) { callbackState.Timer.Dispose(); }
        }, state, millisecond, -1);
}

يجب أن يمنع القفل رد الاتصال الموقت من محاولة تحرير الموقت قبل Timer الحقل الذي تم تعيينه.


إضافة: كما أشار المعلق، إذا action() يفعل شيئا مع واجهة المستخدم، ثم باستخدام System.Windows.Forms.Timer ربما رهان أفضل، حيث سيتم تشغيل رد الاتصال على مؤشر ترابط UI. ومع ذلك، إذا لم يكن هذا هو الحال، و Thread.Sleep ضد. Threading.Timer, Threading.Timer هي الطريق للذهاب.

نصائح أخرى

استعمال ThreadPool.RegisterWaitForSingleObject بدلا من الموقت:

//Wait 5 seconds then print out to console. 
//You can replace AutoResetEvent with a Semaphore or EventWaitHandle if you want to execute the command on those events and/or the timeout
System.Threading.ThreadPool.RegisterWaitForSingleObject(new AutoResetEvent(false), (state, bTimeout) => Console.WriteLine(state), "This is my state variable", TimeSpan.FromSeconds(5), true);

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

على سبيل المثال، كنت أعمل على عميل POP3 حيث كان المبرمج يستخدم Read.Sleep (1000) للانتظار أثناء استرداد المقبس البريد. في هذه الحالة، كان من الأفضل رفع معالج حدث إلى المقبس وإعدام البرنامج المستمر بعد الانتهاء من المقبس.

أتذكر تنفيذ حل مشابه ل Oric's One. هذا هو العمل واحد؛)

class OneTimer
    {
        // Created by Roy Feintuch 2009
        // Basically we wrap a timer object in order to send itself as a context in order to dispose it after the cb invocation finished. This solves the problem of timer being GCed because going out of context
        public static void DoOneTime(ThreadStart cb, TimeSpan dueTime)
        {
            var td = new TimerDisposer();
            var timer = new Timer(myTdToKill =>
            {
                try
                {
                    cb();
                }
                catch (Exception ex)
                {
                    Trace.WriteLine(string.Format("[DoOneTime] Error occured while invoking delegate. {0}", ex), "[OneTimer]");
                }
                finally
                {
                    ((TimerDisposer)myTdToKill).InternalTimer.Dispose();
                }
            },
                        td, dueTime, TimeSpan.FromMilliseconds(-1));

            td.InternalTimer = timer;
        }
    }

    class TimerDisposer
    {
        public Timer InternalTimer { get; set; }
    }

اللحم البقري الوحيد الذي لدي مع system.timer هو أن معظم الوقت رأيته يستخدم التأخير الطويل (ساعات، دقائق) في خدمات الاقتراع والمطورين غالبا ما ينسون إطلاق الحدث قبل يبدأون الموقت. هذا يعني أنه إذا قمت ببدء تشغيل التطبيق أو الخدمة، يجب أن أنتظر حتى ينقلج مؤقت (ساعات، دقائق) قبل تنفيذها بالفعل.

بالتأكيد، هذه ليست مشكلة مع الموقت، لكنني أعتقد أن ذلك غالبا ما تستخدم بشكل غير صحيح لأنها سهلة للغاية سوء الاستخدام.

@ miniscalope لا لا تستخدم threadpool.registerwaitforsingleObject بدلا من الموقت، System.Threading.timer سيقوم قائمة إعادة ربط رد الاتصال على خيط تجمع مؤلم عندما انقض الوقت ولا يتطلب مقبض انتظار، انتظر كائن واحد اربط مؤشر ترابط Threadpool في انتظار الإشارة إلى الحدث أو المهلة التي يجب انتهاء المهلة قبل أن يستدعي الموضوع رد الاتصال.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top