سؤال

أنا أكتب خدمة Windows تقوم بتشغيل نشاط متغير الطول على فترات (فحص قاعدة البيانات وتحديثها).أحتاج إلى تشغيل هذه المهمة بشكل متكرر، ولكن الكود الذي يجب التعامل معه ليس آمنًا للتشغيل عدة مرات بشكل متزامن.

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

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

المحلول

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

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

مثال:

System.Threading.Timer timer = null;

timer = new System.Threading.Timer((g) =>
  {
      Console.WriteLine(1); //do whatever

      timer.Change(5000, Timeout.Infinite);
  }, null, 0, Timeout.Infinite);

العمل فورا ..... إنهاء ... انتظر 5 ثوان .... العمل فورا ..... إنهاء ... الانتظار 5 ثوان ....

نصائح أخرى

وكنت استخدام Monitor.TryEnter في التعليمات البرمجية المنقضي الخاص بك:

if (Monitor.TryEnter(lockobj))
{
  try
  {
    // we got the lock, do your work
  }
  finally
  {
     Monitor.Exit(lockobj);
  }
}
else
{
  // another elapsed has the lock
}

وأنا أفضل System.Threading.Timer لمثل هذه الامور، لأنني لم يكن لديك للذهاب من خلال هذا الحدث آلية التعامل مع:

Timer UpdateTimer = new Timer(UpdateCallback, null, 30000, 30000);

object updateLock = new object();
void UpdateCallback(object state)
{
    if (Monitor.TryEnter(updateLock))
    {
        try
        {
            // do stuff here
        }
        finally
        {
            Monitor.Exit(updateLock);
        }
    }
    else
    {
        // previous timer tick took too long.
        // so do nothing this time through.
    }
}

ويمكنك قضاء على الحاجة لتأمين بجعل الموقت على بعد طلقة واحدة وإعادة البدء بتشغيله بعد كل تحديث:

// Initialize timer as a one-shot
Timer UpdateTimer = new Timer(UpdateCallback, null, 30000, Timeout.Infinite);

void UpdateCallback(object state)
{
    // do stuff here
    // re-enable the timer
    UpdateTimer.Change(30000, Timeout.Infinite);
}

وبدلا من قفل (والتي يمكن أن تسبب كل من المسح في الوقت المناسب لننتظر وكومة في نهاية المطاف). هل يمكن أن تبدأ المسح الضوئي / التحديث في موضوع وبعد ذلك فقط القيام فحص لمعرفة ما إذا كان موضوع لا يزال على قيد الحياة.

Thread updateDBThread = new Thread(MyUpdateMethod);

...

private void timer_Elapsed(object sender, ElapsedEventArgs e)
{
    if(!updateDBThread.IsAlive)
        updateDBThread.Start();
}

يمكنك استخدام AutoResetEvent على النحو التالي:

// Somewhere else in the code
using System;
using System.Threading;

// In the class or whever appropriate
static AutoResetEvent autoEvent = new AutoResetEvent(false);

void MyWorkerThread()
{
   while(1)
   {
     // Wait for work method to signal.
        if(autoEvent.WaitOne(30000, false))
        {
            // Signalled time to quit
            return;
        }
        else
        {
            // grab a lock
            // do the work
            // Whatever...
        }
   }
}

الحل "الأذكى" قليلاً هو كما يلي في الكود الزائف:

using System;
using System.Diagnostics;
using System.Threading;

// In the class or whever appropriate
static AutoResetEvent autoEvent = new AutoResetEvent(false);

void MyWorkerThread()
{
  Stopwatch stopWatch = new Stopwatch();
  TimeSpan Second30 = new TimeSpan(0,0,30);
  TimeSpan SecondsZero = new TimeSpan(0);
  TimeSpan waitTime = Second30 - SecondsZero;
  TimeSpan interval;

  while(1)
  {
    // Wait for work method to signal.
    if(autoEvent.WaitOne(waitTime, false))
    {
        // Signalled time to quit
        return;
    }
    else
    {
        stopWatch.Start();
        // grab a lock
        // do the work
        // Whatever...
        stopwatch.stop();
        interval = stopwatch.Elapsed;
        if (interval < Seconds30)
        {
           waitTime = Seconds30 - interval;
        }
        else
        {
           waitTime = SecondsZero;
        }
     }
   }
 }

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


يحرر

يجب أن أضيف أن هذا الرمز يفترض أن لديك واحدًا فقط من MyWorkerThreads() قيد التشغيل، وإلا فسيتم تشغيلهما بشكل متزامن.

ولقد استعملت مزامنة عندما كنت أريد إعدام واحدة:

    private void OnMsgTimer(object sender, ElapsedEventArgs args)
    {
        // mutex creates a single instance in this application
        bool wasMutexCreatedNew = false;
        using(Mutex onlyOne = new Mutex(true, GetMutexName(), out wasMutexCreatedNew))
        {
            if (wasMutexCreatedNew)
            {
                try
                {
                      //<your code here>
                }
                finally
                {
                    onlyOne.ReleaseMutex();
                }
            }
        }

    }

وآسف أنا في وقت متأخر جدا ... سوف تحتاج إلى توفير اسم كائن مزامنة كجزء من GetMutexName () استدعاء الأسلوب.

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