سؤال

لدينا خدمة Windows مكتوبة بلغة C#.تولد الخدمة موضوعًا يقوم بهذا:

private void ThreadWorkerFunction()
{
  while(false == _stop) // stop flag set by other thread
  {
    try
    {
      openConnection();

      doStuff();

      closeConnection();
    }
    catch (Exception ex)
    {
      log.Error("Something went wrong.", ex);

      Thread.Sleep(TimeSpan.FromMinutes(10));
    }
  }
}

لقد وضعنا Thread.Sleep بعد عدة مرات عندما اختفت قاعدة البيانات وعدنا إلى ملفات سجلات 3 جيجا بايت مليئة بأخطاء اتصال قاعدة البيانات.

كان هذا يعمل بشكل جيد لعدة أشهر، ولكننا رأينا مؤخرًا بعض الحالات حيث تقوم عبارة log.Error() بتسجيل "System.InvalidOperationException:لقد اكتملت معاملة Sql هذه؛لم يعد صالحًا للاستخدام" استثناءً ثم لا يعود أبدًا.يمكن ترك الخدمة قيد التشغيل لعدة أيام ولكن لن يتم تسجيل أي شيء آخر.

بعد أن قمت ببعض القراءة، أعلم أن Thread.Sleep ليس مثاليًا، ولكن لماذا ببساطة لن يعود أبدًا؟

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

المحلول

حفر ومعرفة؟عصا المصحح على هذا اللقيط!

أستطيع أن أرى على الأقل الاحتمالات التالية:

  1. توقف نظام التسجيل؛
  2. تم خروج الخيط بشكل جيد ولكن الخدمة لا تزال قيد التشغيل لأن جزءًا آخر به خطأ منطقي.

وربما، ولكن من المؤكد لا، ما يلي:

  • توقف النوم ().

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

نصائح أخرى

لقد وضعنا Thread.Sleep بعد عدة مرات عندما اختفت قاعدة البيانات وعدنا إلى ملفات سجلات 3 جيجا بايت مليئة بأخطاء اتصال قاعدة البيانات.

أعتقد أن الخيار الأفضل هو جعل نظام التسجيل الخاص بك يحجز التكرارات، حتى يتمكن من كتابة شيء مثل، "تم تكرار الرسالة السابقة N مرات".

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

عندما تقول أنه يتم الإبلاغ عن الخطأ الذي تصفه، هل تقصد ذلك هذا المعالج يتم الإبلاغ عن الخطأ؟السبب غير الواضح بالنسبة لي هو أنك تقول في مقتطف الشفرة "حدث خطأ ما"، لكنك لم تذكر ذلك في وصفك؛لا أريد أن يكون هذا شيئًا سخيفًا جدًا حيث يتم اكتشاف الاستثناء في مكان آخر، ويعلق الكود في مكان آخر غير وضع النوم.

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

bool hadError = false;
try {
  ...
} catch (...) {
  hadError = true;
}
if (hadError)
  Thread.Sleep(...);

يبدو أن مقاطعة مؤشرات الترابط لا تعمل في سياق معالج الاستثناء.

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

من الكود الذي نشرته، ليس من الواضح أنه بعد طرح استثناء، سيكون النظام قادرًا بالتأكيد على إعادة التشغيل - على سبيل المثال.إذا جاء الاستثناء من doStuff()، فسيعود تدفق التحكم (بعد الانتظار لمدة 10 دقائق) إلى openConnection()، دون المرور عبر CloseConnection().

ولكن كما قال الآخرون، ما عليك سوى إرفاق مصحح الأخطاء والعثور على مكانه الفعلي.

جرب Thread.Sleep(10*60*1000)

لم أفهم مطلقًا ما الذي يحدث، ولكن يبدو أن الأمر مرتبط بطرح ThreadInterruptedExceptions أثناء النوم لمدة 10 دقائق، لذلك قمت بالتغيير إلى الكود:

private void ThreadWorkerFunction()
{
  DateTime? timeout = null;

  while (!_stop)
  {
    try
    {
      if (timeout == null || timeout < DateTime.Now)
      {
        openDatabaseConnections();

        doStuff();

        closeDatabaseConnections();
      }
      else
      {
        Thread.Sleep(1000);
      }
    }
    catch (ThreadInterruptedException tiex)
    {
      log.Error("The worker thread was interrupted... ignoring.", tiex);
    }
    catch (Exception ex)
    {
      log.Error("Something went wrong.", ex);

      timeout = DateTime.Now + TimeSpan.FromMinutes(10);
    }
  }
}

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

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

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