السكون والإسبات وModern Standby والتطبيقات طويلة التشغيل ── منع «التوقّف في منتصف الليل» بالتصميم

· آخر تحديث: · · السكون, Modern Standby, إدارة الطاقة, SetThreadExecutionState, التشغيل طويل الأمد, المؤقّتات, تطبيق مقيم, C#, .NET, تحقيق الأخطاء, تطوير Windows, الاستشارات التقنية

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

والمعضلة هي أنّ «السكون» ليس نوعاً واحداً رغم أنّه يُذكَر ككلمة واحدة. سكون S3 التقليديّ، وحالة الإسبات (S4)، وModern Standby (S0 low power idle) السائد في الحواسيب المحمولة حديثاً ─ يختلف فيها شكل التوقّف كما يراه التطبيق، كما يختلف مدى فاعليّة سبل التعامل معها. وModern Standby على وجه الخصوص يُساء فهمه غالباً بسبب الترويج له بأنّ «النظام يستمرّ بالعمل حتّى أثناء السكون»، بينما الحقيقة أنّ تطبيقات سطح المكتب تُوقَف فيه بفعّاليّة أكبر.

في هذا المقال، وبعد الإحاطة بالحدّ الأدنى اللازم عن أنواع السكون وسلوك التطبيق أثناءه، سنرتّب كيفيّة التمييز بين ثلاثة تصاميم: «منع السكون»، و«افتراض حدوث السكون»، و«الإيقاظ في وقت محدَّد»، مع أمثلة تنفيذ وجدول قرار.

1. الخلاصة أوّلاً

  • سكون Windows يشمل S3 التقليديّ وحالة الإسبات (S4) وModern Standby (S0 low power idle)، والأجهزة المتوافقة مع Modern Standby لا تدعم S1 إلى S3. يمكن معرفة نوع حاسوبك عبر powercfg /a.123
  • حتّى أثناء Modern Standby، لا تستمرّ تطبيقات سطح المكتب بالعمل. يقوم DAM (Desktop Activity Moderator) بتعليق (suspend) خيوط عمليّات سطح المكتب (وتُقيَّد خدمات Session 0 بالخنق throttling). لا تُصمِّم بافتراض «بما أنّه S0 فلا بدّ أنّه يعمل».4
  • أثناء السكون لا تُنفَّذ الخيوط، وتختلف طريقة عدّ مهلة المؤقّتات أيضاً باختلاف جيل الـ API. ابتداءً من Windows 8، المؤقّتات والانتظارات ذات التحديد النسبيّ (التحديد النسبيّ في SetWaitableTimer، وSleepEx وما شابه) لا تتقدّم عدّاً أثناء السكون، ويُنقَل الوقت المتبقّي إلى ما بعد العودة.56 مؤقّتات .NET كانت حتّى .NET 10 تُنفَّذ بعدّ وقت السكون (إذا حانت المهلة أثناء السكون، يُطلَق المؤقّت فور العودة)، لكن ابتداءً من .NET 11 يتغيّر الأسلوب إلى عدم العدّ.6 كما تختلط واجهات APIs لقياس الوقت المنقضي بين ما يتضمّن وقت السكون (GetTickCount، وQueryPerformanceCounter وهو أساس Stopwatch) وما لا يتضمّنه (QueryUnbiasedInterruptTime).78
  • اتّصال TCP الذي يصبح خاملاً أثناء السكون قد يُسقَط بصمت بواسطة مهلة الخمول لدى أجهزة وسيطة كـ NAT وجدار الحماية وموازن التحميل (مثال: Azure Load Balancer يُسقِط الاتّصال دون إشعار بعد 4 دقائق افتراضيّاً). صمِّم على افتراض إعادة الاتّصال بعد العودة.94
  • الطريقة الصحيحة لكبح السكون هي SetThreadExecutionState(ES_CONTINUOUS | ES_SYSTEM_REQUIRED). لكنّها تعمل فقط على السكون التلقائيّ الناتج عن مهلة عدم النشاط، ولا تمنع السكون الذي يطلبه المستخدم عبر زرّ الطاقة أو إغلاق الغطاء. اضبطها فقط في المنطقة الزمنيّة اللازمة، ثمّ امسحها دائماً عند الانتهاء.1011
  • يمكن التحقّق من فاعليّة الكبح عبر powercfg /requests. أمّا واجهات PowerCreateRequest فيمكنها إرفاق نصّ سبب بالطلب، فيظهر في هذه القائمة، ما يقوّي التحقيق التشغيليّ.121314
  • إذا اخترتَ افتراض حدوث السكون، فالكشف عنه في .NET يكون عبر SystemEvents.PowerModeChanged، وفي Win32 عبر WM_POWERBROADCAST (PBT_APMSUSPEND / PBT_APMRESUMEAUTOMATIC). مهلة إشعار التعليق (suspend) لا تتجاوز نحو ثانيتين لكلّ تطبيق، وقد يحدث السكون دون إشعار أصلاً عند نفاد البطاريّة الحرِج.15161718
  • للتشغيل المضمون في وقت محدَّد، الخيار الأوّل هو ميزة «تنشيط الحاسوب من السكون عند تنفيذ المهمّة» (WakeToRun) في جدولة المهام، والتي تُبقي النظام مستيقظاً حتّى اكتمال المهمّة. لكنّها تعتمد على السماح بمؤقّتات الإيقاظ في خيارات الطاقة، لذا فإنّ التحقّق الفعليّ من الاستيقاظ على الجهاز ضروريّ.1920

2. سكون Windows ليس نوعاً واحداً

بدايةً، لنحط بالحالات الثلاث التي تشكّل أساس التفكير في سبل التعامل.

الحالة الاسم الشائع المحتوى من منظور التطبيق
S3 السكون التقليديّ توقّف المعالج، وتزويد الذاكرة العشوائيّة (RAM) فقط بالطاقة للاحتفاظ بالحالة لا تعمل أيّ معالجة حسابيّة إطلاقاً1
S4 حالة الإسبات كتابة محتوى الذاكرة في ملفّ إسبات ثمّ قطع الطاقة كما سبق. العودة عبر استعادة الملفّ1
S0 low power idle Modern Standby يستمرّ النظام بالعمل جزئيّاً بطاقة منخفضة، ويعود فوراً يوقِف DAM تطبيقات سطح المكتب24

S3 وS4 هما «حالة تبدو فيها المنظومة مطفأة، ولا يُنفَّذ فيها أيّ مهمّة حسابيّة».1 أمّا Modern Standby فأسلوب أقرب إلى نموذج طاقة الهواتف الذكيّة، حيث يبقى الاتّصال بالشبكة قائماً حتّى مع إطفاء الشاشة، وينتظر النظام بطاقة منخفضة، ويعود خلال أقلّ من ثانية واحدة من الضغط على زرّ الطاقة. الأجهزة الداعمة لـ Modern Standby لا تستخدم S1 إلى S3.221

المهمّ هنا هو DAM (Desktop Activity Moderator) الموجود في الأجهزة المتوافقة مع Modern Standby. DAM آليّة تكبح تنفيذ تطبيقات سطح المكتب عند الدخول في الاستعداد (standby) لتصبح مكافئة لسكون S3، حيث تُعلَّق كلّ خيوط عمليّات الجلسة التفاعليّة، بينما تُخنَق خدمات Session 0 (تُعلَّق معظم الوقت وتُنفَّذ بشكل متقطّع فقط).4 بعبارة أخرى، التوقّع بأنّ «Modern Standby يعني استمرار عمل التطبيق أثناء السكون» معكوس تماماً؛ من منظور التطبيق يتوقّف كما في S3، بل إنّه على خلاف S3 يصبح «النظام نفسه يعمل بينما التطبيق وحده متوقّف»، ما يظهر كانزياح في التوقيت أو تضارب في سلوك المؤقّتات. من الأسلم فهم هذا الأمر مسبقاً.4

يمكن معرفة أيّ الأسلوبين يستخدمه حاسوبك (أو حاسوب موقع العميل) دون الحاجة إلى صلاحيّة مسؤول، عبر الأمر التالي.3

> powercfg /a
حالات السكون التالية متوفّرة على هذا النظام:
    استعداد (S0 طاقة منخفضة خاملة) متّصل بالشبكة
    إسبات
    ...

إذا ظهرت استعداد (S3) فالجهاز من نوع S3، وإذا ظهرت S0 طاقة منخفضة خاملة فهو جهاز Modern Standby. في استشارات من نوع «بدأت السجلّات تفقد أجزاءً بعد التحويل إلى حاسوب محمول»، نتحقّق من هذا أوّلاً.

3. ماذا يحدث للتطبيق أثناء السكون وبعد العودة منه

3.1. الخيوط والمؤقّتات

أثناء السكون (S3/S4، وأثناء تعليق DAM) لا تُنفَّذ الخيوط.14 وما يُغفَل عنه غالباً هو كيفيّة عدّ «مهلة» المؤقّتات والانتظارات لوقت السكون، وهذا يختلف باختلاف طبقة الـ API.

  • مؤقّت Win32 النسبيّ (التحديد النسبيّ في SetWaitableTimer / SetWaitableTimerEx) كان حتّى Windows 7 يتضمّن في العدّ زمن حالة الطاقة المنخفضة (يستمرّ العدّ التنازليّ حتّى أثناء السكون)، لكن ابتداءً من Windows 8 لم يعد يتضمّنه. المؤقّت النسبيّ الذي يمتدّ عبر فترة سكون يُطلَق بعد العودة بعد انتظار الوقت المتبقّي.5
  • في المقابل، واجهات الانتظار التي تحدِّد مهلة زمنيّة (SleepEx / WaitForMultipleObjectsEx وغيرها) لم تعد تحسب، ابتداءً من Windows 8، زمن التعطّل كالسكون. يُنقَل الوقت المتبقّي عبر فترة السكون.6
  • يختلف توقيت إطلاق مؤقّتات .NET المُدارة (System.Threading.Timer وغيره) باختلاف إصدار الـ runtime. والسبب أنّ Environment.TickCount64 كان يتضمّن حتّى .NET 10 زمن السكون (يعتمد على GetTickCount64)، ويتغيّر ابتداءً من .NET 11 ليصبح لا يتضمّنه (يعتمد على QueryUnbiasedInterruptTime)، وتنبِّه وثيقة التغيير نفسها إلى أنّ «شيفرة قد لا يُطلَق فيها المؤقّت فور العودة».6

بمعنى آخر، إذا «سكن تطبيق كان يقيس عبر System.Threading.Timer بفاصل 10 ثوانٍ لمدّة 8 ساعات»، فلن يُطلَق المؤقّت 8 ساعات دفعة واحدة على أيّ حال، لكنّ إطلاقه فوراً مرّة واحدة بعد العودة مباشرةً، أو انتظار الوقت المتبقّي أوّلاً ثمّ الإطلاق، يعتمد على طبقة الـ API وإصدار الـ runtime. وفي الحالتين تُفقَد العيّنات أثناء السكون. الأسلم هو عدم الاعتماد على «الإطلاق فور العودة» لبناء معالجة إعادة الترتيب، بل إعادة الجدولة صراحةً عند حدث العودة الموضَّح في الفصل 5.

3.2. انزياح قياس الوقت المنقضي

تختلط في واجهات قياس الوقت المنقضي تلك التي تتضمّن زمن السكون وتلك التي لا تتضمّنه.

API زمن السكون
GetTickCount / GetTickCount64 يتضمّنه7
QueryPerformanceCounter (= أساس Stopwatch في .NET) يتضمّنه (standby وhibernate وconnected standby)8
QueryUnbiasedInterruptTime لا يتضمّنه (زمن working state فقط)227
Environment.TickCount / TickCount64 يتضمّنه حتّى .NET 10 (يعتمد على GetTickCount64)، ويتغيّر ابتداءً من .NET 11 ليصبح لا يتضمّنه (يعتمد على QueryUnbiasedInterruptTime)6

الشيفرة التي تقول «العيّنة التالية بعد مرور 10 ثوانٍ حسب Stopwatch» تصبح، عند امتداد السكون عبرها، «مرّت 8 ساعات و10 ثوانٍ»، وعلى العكس، فإنّ الحكم على مرور الوقت اعتماداً على TickCount يتغيّر سلوكه بعد الانتقال إلى .NET 11. إذا وزَّعتَ الأدوار بحيث تُدار «المدّة التي استغرقتها المعالجة» عبر Stopwatch، و«وقت الساعة الحائطيّة الذي ينبغي التنفيذ عنده لاحقاً» عبر DateTime/DateTimeOffset، مع إعادة أخذ المرجع عند حدث العودة (الفصل 5)، فلن تتأثّر بالسكون ولا بتحديثات الـ runtime. أمّا موضوع تصميم المؤقّتات قصيرة الدورة نفسه، فمشروح في «لماذا يُفضَّل الانتظار المدفوع بالأحداث على Sleep(1) في Windows».

3.3. اتّصال TCP يموت «بصمت»

أثناء السكون، لا يستطيع التطبيق الاتّصال فيصبح الاتّصال خاملاً. والمشكلة تكمن في الأجهزة الواقعة على المسار. الأجهزة الوسيطة كـ NAT وجدار الحماية وموازن التحميل تُسقِط التدفّقات الخاملة بعد مهلة زمنيّة، لكن في كثير من التكوينات يتمّ الإسقاط دون إشعار أيّ من الطرفين، أي بصمت. فمثلاً، السلوك الافتراضيّ لـ Azure Load Balancer هو «إسقاط التدفّق بصمت عند بلوغ مهلة الخمول (4 دقائق افتراضيّاً)».9 وتوجد مهلات مشابهة في أجهزة التوجيه الداخليّة أو مكدّس TCP المدمج في الأجهزة.

ونتيجةً لذلك، يبقى مقبس (socket) التطبيق بعد العودة يبدو سليماً ظاهريّاً، ثمّ يصدر خطأ عند الإرسال التالي أو يتجمّد حتّى بلوغ المهلة الزمنيّة أثناء انتظار الاستجابة. حتّى وثائق DAM تنصّ صراحةً على ضرورة مراعاة أثر تعليق العمليّة على عمر الاتّصال وعمليّة المصافحة (handshake).4 كذلك، في أجهزة Modern Standby العاملة بالبطاريّة، يُوقَف نشاط الشبكة أثناء السكون نفسه بالإعداد الافتراضيّ (Adaptive Connected Standby).23 القاعدة المتّبعة هي الشكّ في الاتّصال والتخلّص منه وإعادة الاتّصال فور استقبال حدث العودة. لتشخيص مشكلة الاتّصال «الذي يبدو حيّاً لكنّه ميّت»، راجع أيضاً «سبب توقّف اتّصال كاميرا صناعيّة بسبب إعادة إرسال TCP وطريقة التشخيص».

4. «منع» السكون ── SetThreadExecutionState وطلبات الطاقة

عندما يكون السكون مزعجاً فقط أثناء ساعات القياس، فإنّ الطريقة الصحيحة هي SetThreadExecutionState. تُستدعى من C# عبر P/Invoke.

using System.Runtime.InteropServices;

internal static class PowerGuard
{
    [Flags]
    private enum EXECUTION_STATE : uint
    {
        ES_CONTINUOUS       = 0x80000000,
        ES_SYSTEM_REQUIRED  = 0x00000001,
        ES_DISPLAY_REQUIRED = 0x00000002,
    }

    [DllImport("kernel32.dll", SetLastError = true)]
    private static extern EXECUTION_STATE SetThreadExecutionState(EXECUTION_STATE esFlags);

    /// <summary>يُستدعى عند بدء القياس: يمنع السكون التلقائيّ (استدعِه من نفس الخيط الذي يستدعي End)</summary>
    public static void Begin() =>
        SetThreadExecutionState(EXECUTION_STATE.ES_CONTINUOUS |
                                EXECUTION_STATE.ES_SYSTEM_REQUIRED);

    /// <summary>يجب استدعاؤه دائماً عند انتهاء القياس: يلغي المنع (استدعِه من نفس الخيط الذي يستدعي Begin)</summary>
    public static void End() =>
        SetThreadExecutionState(EXECUTION_STATE.ES_CONTINUOUS);
}

المواصفات التي يجب معرفتها هي التالية.

  • إضافة ES_CONTINUOUS تجعل الأثر مستمرّاً حتّى الاستدعاء التالي المصحوب بـ ES_CONTINUOUS. أمّا دونها، فلا يحدث سوى إعادة ضبط مؤقّت الخمول مرّة واحدة، فإن أردتَ الاستمراريّة يلزم الاستدعاء دوريّاً.10
  • ES_SYSTEM_REQUIRED يكبح سكون النظام، وES_DISPLAY_REQUIRED يكبح إطفاء الشاشة. للقياس في الخلفيّة، يكفي الأوّل فقط. رفع ES_DISPLAY_REQUIRED أيضاً في حين يجوز إطفاء الشاشة هدر للطاقة.10
  • لا يمكن منع السكون الناتج عن ضغط المستخدم زرّ الطاقة أو إغلاق الغطاء. هذه الدالّة تعمل فقط على السكون التلقائيّ الناتج عن مهلة عدم النشاط. وتنصّ الوثائق الرسميّة صراحةً على وجوب احترام إجراء المستخدم الصريح.10
  • يعدّ النظام الخيوط التي استدعت SetThreadExecutionState، وعندما يصل العدّاد إلى صفر ولا يوجد إدخال من المستخدم، يدخل في السكون.11 إذا انتهت العمليّة بشكل غير طبيعيّ، يختفي الكبح أيضاً، فالمخاوف من «حاسوب لا ينام أبداً بسبب نسيان إلغاء الكبح» تُحلّ بإعادة التشغيل، لكن بالمقابل هذا لا يضمن أيّ مقاومة للانهيار.
  • كما يشير الاسم، ما تضبطه هذه الدالّة هو حالة تنفيذ الخيط المستدعي (calling thread).10 نفِّذ الضبط والإلغاء من الخيط نفسه. وبما أنّ استمرار async/await قد يُنفَّذ على خيط آخر من مجمّع الخيوط (thread pool)، فإنّ التنفيذ الذي يستدعي Begin() وEnd() عبر await بينهما قد يجعل الإلغاء يضرب فراغاً على خيط مختلف عن الذي رفع كبح السكون، فيبقى الكبح قائماً طوال بقاء الخيط الأصليّ حيّاً. الأسلم هو الاستدعاء من خيط مضمون الهويّة كخيط الواجهة (UI thread)، وإذا احتجتَ تصميماً يعبر الخيوط، استخدم واجهة طلبات الطاقة المعتمِدة على المقابض (handle) المذكورة لاحقاً.

منذ Windows 7 فما بعده، توجد أيضاً واجهة أحدث للغرض نفسه، وهي طلبات الطاقة (PowerCreateRequest / PowerSetRequest / PowerClearRequest). ميزتها العمليّة هي إمكانيّة تمرير نصّ سبب عبر REASON_CONTEXT عند إنشاء الطلب، ومن أنواع الطلبات PowerRequestSystemRequired، إضافةً إلى PowerRequestExecutionRequired الذي يكبح تعليق العمليّة في أجهزة Modern Standby.1413 وأفضل ممارسة رسميّة هي «تعيين (Set) مباشرةً قبل السيناريو، ومسح (Clear) فوريّ عند الانتهاء، وتنظيف المقبض (handle) قبل إنهاء العمليّة».13

لكن توجد قيود مهمّة على أجهزة Modern Standby. في منظومات Modern Standby العاملة بالبطاريّة، تُقطَع طلبات SystemRequired/ExecutionRequired بعد 5 دقائق من تجاوز مهلة السكون. كذلك، وبغضّ النظر عن مصدر الطاقة، ينتهي الطلب عند دخول السكون بفعل إجراء المستخدم (زرّ الطاقة، إغلاق الغطاء، سكون قائمة ابدأ).13 بمعنى آخر، «الاستمرار بالعمل على حاسوب محمول حتّى مع إغلاق الغطاء وحتّى على البطاريّة» أمرٌ لا يمكن تحقيقه بجهد التطبيق وحده. مثل هذه المتطلّبات تُضمَن عبر إعدادات الطاقة والتشغيل (إعداد عدم السكون عند إغلاق الغطاء، والتغذية بالتيّار المتردّد).

يمكن التحقّق من فاعليّة الكبح فعليّاً عبر موجِّه أوامر بصلاحيّة المسؤول.

> powercfg /requests
SYSTEM:
[PROCESS] \Device\HarddiskVolume3\Apps\SensorLogger.exe

الأمر powercfg /requests يسرد طلبات الطاقة التي تعيق السكون أو إطفاء الشاشة، ويمكن استخدامه في التحقيق حول «حاسوب لا ينام دون سبب واضح»، وكذلك في التحقّق من كبح تطبيقك نفسه.12 وفي المقابل، اعلم أنّ المسؤول يستطيع ضبط إعداد عبر powercfg /requestsoverride لتجاهل طلبات عمليّة محدَّدة.12 فأساس التصميم هو أنّ واجهة الكبح «طلب» وليست أمراً مطلقاً.

أخيراً، كلمة عن حسن السلوك. التنفيذ الذي يرفع ES_CONTINUOUS | ES_SYSTEM_REQUIRED طوال فترة تشغيل التطبيق يعني أنّ التطبيق المقيم يقتل خطّة الطاقة التي ضبطها المستخدم بشكل دائم. في الحاسوب المحمول يستنزف البطاريّة، وفي الحاسوب المشترك يؤثّر على استخدامات أخرى. المبدأ هو قصر الكبح على «المدّة التي تعمل فيها فعليّاً المعالجة التي يُزعِج توقّفها بالسكون» فقط (حتّى مثال الوثائق الرسميّة هو «الضبط عند بدء التسجيل، والمسح عند اكتمال التسجيل»10). وكحلّ مؤقّت من جهة المستخدم، توجد أداة PowerToys Awake، التي تعمل داخليّاً بالآليّة نفسها (خيط يطلب حالة التنفيذ).24 ويمكن استخدام هذا أيضاً كخطّ فاصل بين تنفيذ الكبح داخل التطبيق أو تركه لأداة تشغيليّة.

5. «افتراض» حدوث السكون ── الكشف وإعادة الاتّصال وتسجيل الفقدان

في كثير من تطبيقات المراقبة والتجميع المقيمة، يكون التصميم الأسلم هو التعايش مع السكون بدل منعه. والمطلوب ثلاث نقاط: «المعرفة قبل الدخول فيه»، و«معرفة العودة»، و«إعادة الترتيب بعد العودة».

في .NET، المدخل هو Microsoft.Win32.SystemEvents.PowerModeChanged.15

using Microsoft.Win32;

SystemEvents.PowerModeChanged += OnPowerModeChanged;

private void OnPowerModeChanged(object sender, PowerModeChangedEventArgs e)
{
    switch (e.Mode)
    {
        case PowerModes.Suspend:
            // مهلة السماح قصيرة: اقتصر على تفريغ المخازن المؤقّتة (buffers) وتسجيل وقت توقّف القياس فقط
            _logger.Info("suspend at {0:O}", DateTimeOffset.Now);
            _collector.Pause();
            break;

        case PowerModes.Resume:
            // 1) سجِّل فترة الفقدان نفسها كبيانات
            _logger.Info("resume at {0:O}", DateTimeOffset.Now);
            // 2) افترض أنّ الاتّصال ميّت، تخلَّص منه، وأعِد الاتّصال
            _connection.Reset();
            // 3) أعِد حساب الجدول اعتماداً على الساعة الحائطيّة وأعِد ضبط المؤقّتات
            _scheduler.Rebase(DateTimeOffset.Now);
            // 4) أعِد استئناف التجميع صراحةً فقط بعد اكتمال إعادة الترتيب (لا تتركه متوقّفاً)
            _collector.Resume();
            break;
    }
}

توجد نقطتان منصوص عليهما رسميّاً بخصوص هذا الحدث. الأولى أنّه لا يحدث ما لم تكن مضخّة الرسائل (message pump) تعمل (تحتاج خدمات Windows إلى نموذج مخفيّ (hidden form) أو نحوه). والثانية أنّه حدث static، فإهمال إلغاء الاشتراك يسبِّب تسريباً (leak).15 يمكن استخدامه ببساطة في تطبيقات GUI، لكن في تطبيقات التجميع من نوع وحدة تحكّم/خدمة، إمّا أن تُهيّئ بنفسك نافذة رسائل (message window) تستقبل WM_POWERBROADCAST الخاصّة بـ Win32، أو تستخدم PowerRegisterSuspendResumeNotification التي تستقبل استدعاءً معاوداً (callback) دون HWND (وهذا المسار هو أيضاً مسار الإشعار في بيئة DAM).416

لنحط أيضاً بمعنى الأحداث على مستوى Win32.

  • PBT_APMSUSPEND: إشعار قبل السكون مباشرةً. المهلة نحو ثانيتين فقط لكلّ تطبيق، وقد يقاطع النظام المعالجة عند التجاوز. اقصر ما يُنفَّذ هنا على التفريغ (flush) وتسجيل الوقت فحسب، ولا تكتب معالجة تستغرق وقتاً كالتنظيف عبر الشبكة.1718
  • PBT_APMRESUMEAUTOMATIC: إشعار يصل حتماً في كلّ عودة. اكتب إعادة الاتّصال وإعادة الجدولة هنا.25
  • PBT_APMRESUMESUSPEND: يصل بعد PBT_APMRESUMEAUTOMATIC عند العودة بفعل المستخدم (أو عند عودة المستخدم). لا يصل عند العودة التلقائيّة كالإيقاظ عن بُعد، لذا إذا كتبتَ «معالجة العودة» هنا فقط، فلن تُنفَّذ إعادة الترتيب عند العودة دون وجود مستخدم.26
  • إضافةً إلى ذلك، لا يصل الإشعار المسبق أصلاً عند دخول السكون الحرِج كنفاد البطاريّة الوشيك.18 اكتب جانب Resume بشكل مُتَحايث (idempotent) بحيث تصحّ معالجة العودة حتّى في حالات عدم استقبال حدث Suspend.

ثمّة أمر آخر لا يقلّ أهميّة عن معالجة العودة، وهو تسجيل الفقدان بوصفه فقداناً. البيانات المُجمَّعة الممتدّة عبر فترة سكون ليست «قيمة غير موجودة»، بل «لم تُقَس لأنّ النظام كان متوقّفاً»، وإذا تُرِك ذلك النطاق مسجَّلاً بوقتَي suspend/resume في كلّ من السجلّ والبيانات، فلن يخلط من يرى الفراغ في الرسم البيانيّ لاحقاً بينه وبين عطل. الرؤية حول ما ينبغي تركه في سجلّ تطبيق طويل التشغيل مشروحة أيضاً في «تحقيق انهيار كاميرا صناعيّة بعد تشغيل طويل - قسم تسريب المقابض».

إضافةً لذلك، توجد مشكلة شبيهة بالسكون من نوع «لاحظنا أنّ الأداء تباطأ»، وهي الكبح عبر وضع الكفاءة (Efficiency Mode / EcoQoS) في Windows 11. راجع «ما هو وضع الكفاءة في Windows - أيقونة الورقة الخضراء وكيفيّة إيقافه».

6. التشغيل المضمون في وقت محدَّد ── تنفيذ الإيقاظ في جدولة المهام

تحقيق معالجة مرتبطة بوقت محدَّد، كـ«التجميع والإرسال عند الساعة الثانية صباحاً»، عبر مؤقّت تطبيق مقيم مع كبح السكون ليس أسلوباً جيّداً، لأنّه يقتل السكون طوال الليل. الخيار الأساسيّ لهذا الاستخدام هو ميزة «تنشيط الحاسوب من السكون عند تنفيذ المهمّة» (WakeToRun) في جدولة المهام.

المهمّة المُفعَّل فيها WakeToRun تُوقِظ الحاسوب من السكون أو الإسبات عند وقت التنفيذ، وتُبقي النظام مستيقظاً حتّى اكتمال المهمّة (وإذا كان مستيقظاً أصلاً، تطلب إبقاءه كذلك حتّى الاكتمال). قد تبقى الشاشة مطفأة عند الاستيقاظ، وهذا أمر طبيعيّ.19

لكن يوجد شرط لتحقّق الاستيقاظ. كما ورد في دليل استكشاف الأخطاء الرسميّ، فإنّ تفعيل «السماح بمؤقّت الإيقاظ» (Allow Wake Timer) في خيارات الطاقة، وتفعيل إعداد الإيقاظ في BIOS شرطان أساسيّان، وليس نادراً أن تكون الحواسيب المحمولة الحديثة مصمَّمة لتوفير الطاقة بحيث لا تسمح بالإيقاظ.20 يمكن سرد مؤقّتات الإيقاظ المُفعَّلة حاليّاً عبر powercfg /waketimers12، لذا تحقّق دائماً «هل يستيقظ فعلاً» على الجهاز الفعليّ في موقع النشر. إذا أردتَ الإيقاظ من تطبيقك الخاصّ، يوجد أيضاً وسيلة المؤقّت القابل للإيقاظ (wakeable timer) بتمرير TRUE إلى fResume في SetWaitableTimer. في هذه الحالة، يبقى النظام بعد الاستيقاظ التلقائيّ مستيقظاً فقط طوال مؤقّت خمول غير المأهول (دقيقتان على الأقلّ)، وإذا لم يُعلِن التطبيق «قيد الاستخدام» عبر SetThreadExecutionState، يعود للسكون سريعاً. الجمع الصحيح عند طول المعالجة بعد الاستيقاظ هو دمجه مع الكبح الوارد في الفصل 4.27

أمّا تصميم التشغيل نفسه ─ حساب تنفيذ المهمّة، ومشكلة الانتهاء بالرمز 0x1، ومنع التشغيل المتعدّد ─ فمُجمَّع في «مهمّة جدولة المهام لا تُنفَّذ أو تنتهي بالرمز 0x1 ── تحديد السبب وتصميم تشغيل آمن».

7. جدول القرار ── الكبح أم معالجة العودة أم الإيقاظ

التصاميم الثلاثة ليست حصريّة، بل تُدمَج بتحديد الأساسيّ والثانويّ حسب نوع التطبيق.

نوع التطبيق الخيار الأوّل الدمج والملاحظات
القياس وتجميع البيانات (قياس متواصل لساعات إلى أيّام) كبح عبر ES_SYSTEM_REQUIRED أثناء فترة القياس فقط نفِّذ معالجة العودة دائماً أيضاً (لا يمكن منع السكون بفعل المستخدم10). أجهزة Modern Standby العاملة بالبطاريّة تُقطَع بعد 5 دقائق، لذا اجعل التغذية بالتيّار المتردّد شرطاً13
الدفعات الليليّة والإرسال في وقت محدَّد جدولة المهام + WakeToRun19 أكثر توفيراً للطاقة وضماناً من الإقامة الدائمة مع الكبح. التحقّق من السماح بمؤقّت الإيقاظ شرط أساسيّ20
وكيل مراقبة وإشعارات مقيم افتراض حدوث السكون (الكشف عبر PowerModeChanged ← إعادة الاتّصال وإعادة الجدولة وتسجيل الفقدان)15 إذا كان الجهاز مخصَّصاً للمراقبة فقط، عطِّل السكون من جهة خطّة الطاقة لا من التطبيق
أداة تشغيل سطح مكتب (عرض تقديميّ، لوحة معلومات، إلخ) استخدام ES_DISPLAY_REQUIRED مع ES_SYSTEM_REQUIRED أثناء العرض فقط10 امسحه دائماً عند انتهاء العرض. للطرفيّة (terminal) الدائمة العرض، تعامل معها عبر إعدادات الطاقة
التحكّم بالأجهزة 24/7 وحواسيب خطوط الإنتاج تعطيل السكون نفسه عبر إعدادات الطاقة (يُضمَن تشغيليّاً) لا تعتمد على واجهة كبح التطبيق كضمان احتياطيّ. استخدم powercfg /requests في الفحص الدوريّ12

محورا القرار اثنان: «هل توجد فترة يجوز فيها التوقّف» (إن وُجدت فجدولة المهام، وإن لم توجد فإعدادات الطاقة)، و«على حاسوب مَن يعمل» (كلّما كان التطبيق يعمل على جهاز شخصيّ للمستخدم أو حاسوب محمول مشترك، كلّما كان الميل نحو معالجة العودة أفضل من الكبح).

8. الخلاصة

  • يشمل السكون S3 والإسبات (S4) وModern Standby (S0 low power idle)، ويمكن التمييز بينها عبر powercfg /a. حتّى في أجهزة Modern Standby، تُعلَّق تطبيقات سطح المكتب بواسطة DAM، لذا لا يصحّ افتراض «الاستمرار بالعمل أثناء السكون».
  • أثناء السكون لا تتقدّم الخيوط ولا المؤقّتات. ابتداءً من Windows 8، المؤقّتات النسبيّة والانتظارات لا تعدّ زمن السكون وتنقل الوقت المتبقّي، وتصبح مؤقّتات .NET بالسلوك نفسه ابتداءً من .NET 11 (حتّى .NET 10 قد تُطلَق فور العودة). تختلط واجهات قياس الوقت المنقضي بين ما يتضمّن زمن السكون وما لا يتضمّنه. أدِر الوقت المنقضي عبر Stopwatch، والساعة الحائطيّة عبر DateTime، وأعِد أخذ المرجع عند العودة.
  • يموت اتّصال TCP بصمت بسبب مهلة الخمول لدى الأجهزة الوسيطة. القاعدة المتّبعة هي التخلّص منه وإعادة الاتّصال عند حدث العودة.
  • الكبح يكون عبر SetThreadExecutionState(ES_CONTINUOUS | ES_SYSTEM_REQUIRED) أثناء المنطقة الزمنيّة اللازمة فقط. لا يمكن منع السكون بفعل المستخدم، ويُقطَع بعد 5 دقائق في أجهزة Modern Standby العاملة بالبطاريّة. التحقّق عبر powercfg /requests.
  • معالجة العودة عبر SystemEvents.PowerModeChanged / WM_POWERBROADCAST. مهلة إشعار التعليق نحو ثانيتين، ويحدث سكون دون إشعار أحياناً، لذا اكتب جانب Resume بشكل مُتَحايث وسجِّل نطاق الفقدان.
  • الخيار الأساسيّ للتنفيذ في وقت محدَّد هو WakeToRun في جدولة المهام. صمِّم مع مراعاة إعدادات الطاقة لمؤقّت الإيقاظ والتحقّق الفعليّ من الاستيقاظ على الجهاز.

مقالات ذات صلة

مجالات الاستشارة ذات الصلة

تتعامل شركة Komura Soft LLC مع تصميم وتنفيذ تطبيقات Windows طويلة التشغيل، كالقياس والمراقبة وتجميع البيانات، وتحقيق الأعطال الخاصّة بالتشغيل طويل الأمد كـ«التوقّف في منتصف الليل» أو «فقدان أجزاء من السجلّات». لا تتردّدوا في استشارتنا أيضاً حول تشخيص الأعراض التي يصعب تكرارها والمرتبطة بالسكون وإدارة الطاقة.

المراجع

  1. Microsoft Learn، System Sleeping States. حول أنّ S1 إلى S4 حالات سكون لا تُنفَّذ فيها مهامّ حسابيّة، وأنّ S3 يحتفظ بالذاكرة فقط بينما S4 يحفظ في ملفّ إسبات، وإمكانيّة سرد حالات السكون المتاحة عبر powercfg /a.  2 3 4 5

  2. Microsoft Learn، System power states. حول استمرار النظام بالعمل جزئيّاً بطاقة منخفضة في S0 low power idle (Modern Standby)، وعدم استخدام منظومات SoC المتوافقة مع Modern Standby لـ S1 إلى S3، وعدم صدور إشعار عند الانتقالات الحرجة.  2 3

  3. Microsoft Learn، Overview of Modern Standby Testing and Diagnostics. حول إمكانيّة التمييز بدعم Modern Standby (ظهور Standby (S0 Low Power Idle)) عبر powercfg /a.  2

  4. Microsoft Learn، Desktop Activity Moderator. حول كبح DAM لتنفيذ تطبيقات سطح المكتب ليصبح مكافئاً لـ S3، وتعليق كلّ خيوط عمليّات الجلسة التفاعليّة وخنق Session 0، وإشعار WM_POWERBROADCAST قبل التعليق، وضرورة مراعاة سلوك المؤقّتات والتضارب بين زمن التشغيل والساعة الحائطيّة، وعمر الاتّصال.  2 3 4 5 6 7 8

  5. Microsoft Learn، SetWaitableTimerEx function. حول أنّ المؤقّت ذا التحديد الزمنيّ النسبيّ كان يتضمّن حتّى Windows 7 زمن حالة الطاقة المنخفضة (يستمرّ العدّ التنازليّ أثناء السكون)، بينما لا يتضمّنه ابتداءً من Windows 8 (لا يتقدّم العدّ التنازليّ أثناء السكون).  2

  6. Microsoft Learn، Environment.TickCount made consistent with Windows timeout behavior. حول التغيير الجذريّ (breaking change) الذي يجعل Environment.TickCount/TickCount64 يعتمد حتّى .NET 10 على GetTickCount64 (يتضمّن زمن السكون)، ويتغيّر ابتداءً من .NET 11 ليعتمد على QueryUnbiasedInterruptTime (لا يتضمّنه)، وأنّ واجهات الانتظار التي تحدِّد مهلة (SleepEx/WaitForMultipleObjectsEx) لم تعد تحسب زمن التعطّل ابتداءً من Windows 8، واحتمال وجود شيفرة لا يُطلَق فيها المؤقّت فور العودة بسبب هذا التغيير.  2 3 4 5

  7. Microsoft Learn، Windows Time. حول تضمّن الوقت المنقضي في GetTickCount/GetTickCount64 لزمن السكون والإسبات، واقتصار QueryUnbiasedInterruptTime على زمن working state فقط.  2 3

  8. Microsoft Learn، Acquiring high-resolution time stamps. حول اعتماد System.Diagnostics.Stopwatch في الشيفرة المُدارة على QPC كأساس للوقت، وإرجاع QueryPerformanceCounter عدد نبضات (ticks) يتضمّن زمن standby وhibernate وconnected standby.  2

  9. Microsoft Learn، Load Balancer TCP Reset and Idle Timeout. حول أنّ السلوك الافتراضيّ لموازن التحميل هو إسقاط التدفّق بصمت عند بلوغ مهلة الخمول (4 دقائق افتراضيّاً)، وأنّ إرسال إعادة تعيين TCP ميزة تُفعَّل صراحةً، واستخدام TCP keep-alive كإجراء مضادّ.  2

  10. Microsoft Learn، SetThreadExecutionState function. حول معنى ES_CONTINUOUS/ES_SYSTEM_REQUIRED/ES_DISPLAY_REQUIRED، واقتصار الأثر دون ES_CONTINUOUS على إعادة ضبط مؤقّت الخمول فقط، وعدم إمكانيّة منع السكون بفعل المستخدم، ومثال الاستخدام بالضبط أثناء المعالجة اللازمة فقط والمسح بعد الاكتمال.  2 3 4 5 6 7 8

  11. Microsoft Learn، System Sleep Criteria. حول عدّ النظام للتطبيقات/الخيوط التي استدعت SetThreadExecutionState، ودخوله في السكون عندما يصل العدّاد إلى صفر ولا يوجد إدخال من المستخدم.  2

  12. Microsoft Learn، Powercfg command-line options. حول سرد /requests لطلبات الطاقة التي تعيق السكون أو إطفاء الشاشة، وإمكانيّة تجاهل طلبات عمليّة محدَّدة عبر /requestsoverride، وسرد /waketimers للمؤقّتات المُفعَّلة.  2 3 4 5

  13. Microsoft Learn، PowerSetRequest function. حول أنواع الطلبات كـ PowerRequestSystemRequired/PowerRequestExecutionRequired، وقطع الطلب بعد 5 دقائق من تجاوز مهلة السكون في طاقة DC لـ Modern Standby، وانتهاء الطلب عند دخول السكون بفعل المستخدم، وأفضل ممارسة بإرفاق نصّ سبب وSet قبل السيناريو مباشرةً وClear فور الانتهاء.  2 3 4 5

  14. Microsoft Learn، PowerCreateRequest function. حول إنشاء كائن طلب طاقة بتحديد REASON_CONTEXT، وتحريره عبر CloseHandle عند عدم الحاجة إليه.  2

  15. Microsoft Learn، SystemEvents.PowerModeChanged Event. حول كونه حدثاً يحدث عند التعليق/الاستئناف، وعدم حدوثه ما لم تعمل مضخّة الرسائل (تحتاج الخدمات إلى نموذج مخفيّ أو نحوه)، وتسريبه عند عدم إلغاء الاشتراك بسبب كونه حدثاً static.  2 3 4

  16. Microsoft Learn، WM_POWERBROADCAST message. حول إشعار النافذة بأحداث إدارة الطاقة عبر رسالة WM_POWERBROADCAST (PBT_APMSUSPEND/PBT_APMRESUMEAUTOMATIC/PBT_APMRESUMESUSPEND وغيرها).  2

  17. Microsoft Learn، PBT_APMSUSPEND event. حول صدور الإشعار قبل التعليق مباشرةً، وأنّ مهلة المعالجة نحو ثانيتين وقد يقاطعها النظام عند التجاوز.  2

  18. Microsoft Learn، System Power Management Events. حول عدم صدور إشعار مسبق عند التعليق الطارئ كنفاد البطاريّة الحرِج، وانتهاء مهلة معالجة إشعار التعليق عند ثانيتين كحدّ أقصى لكلّ تطبيق.  2 3

  19. Microsoft Learn، ITaskSettings::get_WakeToRun method. حول إيقاظ WakeToRun الحاسوب عند تنفيذ المهمّة وإبقائه مستيقظاً حتّى الاكتمال، وإمكانيّة بقاء الشاشة مطفأة عند الاستيقاظ.  2 3

  20. Microsoft Learn، Automatic maintenance. حول بنود التحقّق عند عدم عمل الاستيقاظ في وقت محدَّد: إعداد الإيقاظ في BIOS، و«Allow Wake Timer» في خيارات الطاقة، وإعداد WakeToRun للمهمّة، وشيوع تكوينات لا تسمح بالاستيقاظ من S3 في الحواسيب المحمولة الحديثة.  2 3

  21. Microsoft Learn، What is Modern Standby. حول تحقيق Modern Standby للتشغيل/الإطفاء الفوريّ مع الحفاظ على الاتّصال بالشبكة وفق نموذج S0 low power idle، وأنّ العودة (من زرّ الطاقة إلى إضاءة الشاشة) أقلّ من ثانية واحدة. 

  22. Microsoft Learn، QueryUnbiasedInterruptTime function. حول عدّ unbiased interrupt time لزمن working state فقط، وعدم تضمّنه زمن السكون والإسبات. 

  23. Microsoft Learn، Modern standby network connectivity. حول إيقاف نشاط الشبكة أثناء السكون عند العمل بالبطاريّة عبر Adaptive Connected Standby ما لم يوجد سيناريو يحتاجه. 

  24. Microsoft Learn، PowerToys Awake utility. حول كونها أداة تُبقي الحاسوب مستيقظاً دون تغيير خطّة الطاقة، وعملها عبر توليد خيط خلفيّة يطلب حالة الجهاز، وعودتها إلى سلوك خطّة الطاقة الاعتياديّ عند الإنهاء. 

  25. Microsoft Learn، PBT_APMRESUMEAUTOMATIC event. حول كونه حدثاً يُبلَّغ حتماً عند كلّ عودة، ولا يدلّ على وجود المستخدم، ويصل PBT_APMRESUMESUSPEND بعده عند اكتشاف نشاط المستخدم. 

  26. Microsoft Learn، PBT_APMRESUMESUSPEND event. حول وصوله بعد PBT_APMRESUMEAUTOMATIC عند العودة بفعل المستخدم أو باكتشافه، واقتصار الإيقاظ عن بُعد على PBT_APMRESUMEAUTOMATIC وحده. 

  27. Microsoft Learn، System Wake-up Events. حول إمكانيّة إيقاظ النظام بالمؤقّت عند تعيين fResume في SetWaitableTimer إلى TRUE، وضبط مؤقّت خمول غير مأهول لدقيقتين على الأقلّ بعد الاستيقاظ التلقائيّ، والعودة إلى السكون ما لم يُظهِر SetThreadExecutionState أنّه قيد الاستخدام. 

أحدث المقالات التي تشترك في نفس الوسوم. عمّق فهمك بمواضيع مرتبطة.

ترتبط هذه المقالة بشكل طبيعي بصفحات الخدمات التالية.

الأسئلة الشائعة

أسئلة شائعة حول موضوع هذه المقالة.

كيف نمنع الحاسوب من الدخول في السكون فقط أثناء تشغيل التطبيق؟
الأساس هو استدعاء SetThreadExecutionState(ES_CONTINUOUS | ES_SYSTEM_REQUIRED) عند بدء المعالجة، ومسحه عبر SetThreadExecutionState(ES_CONTINUOUS) عند انتهائها. إذا أردتَ إبقاء الشاشة مضاءة أيضاً، أضِف ES_DISPLAY_REQUIRED فقط. يعمل هذا فقط على السكون التلقائيّ الناتج عن مهلة عدم النشاط، ولا يمنع السكون الذي يطلبه المستخدم عبر زرّ الطاقة أو إغلاق الغطاء. يمكن التحقّق من فاعليّة الكبح عبر powercfg /requests.
ما هو Modern Standby؟ وما الفرق عن السكون التقليديّ؟
هو أسلوب سكون جديد يُسمّى أيضاً S0 low power idle، تستمرّ فيه المنظومة بالعمل جزئيّاً بطاقة منخفضة، وتتميّز بالعودة الفوريّة. الأجهزة المتوافقة مع Modern Standby لا تدعم سكون S3 التقليديّ. لكنّ «الاستمرار في العمل» يقتصر على الأنشطة التي يسمح بها نظام التشغيل فقط، إذ يوقِف DAM (Desktop Activity Moderator) خيوط تطبيقات سطح المكتب كلّاً على حدة، فتتوقّف من منظور التطبيق كما في S3 تماماً. يمكن معرفة أيّ الأسلوبين يستخدمه حاسوبك عبر powercfg /a.
لماذا يتعذّر استخدام اتّصال TCP بعد العودة من السكون؟
لأنّ التطبيق لا يستطيع الاتّصال أثناء السكون فيصبح الاتّصال خاملاً (idle)، وتقوم الأجهزة الوسيطة على المسار كـ NAT وجدار الحماية وموازن التحميل بإسقاط التدفّق (flow) عند بلوغ مهلة الخمول. كثير من هذه الأجهزة تُسقِط الاتّصال دون إشعار، فيبقى المقبس (socket) في جهة التطبيق يبدو سليماً ظاهريّاً، ولا يظهر الخطأ إلّا عند أوّل إرسال أو استقبال بعد العودة، أو يتجمّد حتّى بلوغ المهلة الزمنيّة. القاعدة المتّبعة هي الشكّ في الاتّصال وإعادة إنشائه فور استقبال حدث العودة.
لتشغيل الدفعة الليليّة (night batch) بشكل مضمون، أيّهما أفضل: كبح السكون أم جدولة المهام؟
الخيار الأوّل هو ميزة «تنشيط الحاسوب من السكون عند تنفيذ المهمّة» (WakeToRun) في جدولة المهام. فهي توقظ الحاسوب في وقت التنفيذ وتُبقيه مستيقظاً حتّى اكتمال المهمّة، فلا حاجة لقتل السكون طوال الليل. لكن إذا كان مؤقّت الإيقاظ (wake timer) معطّلاً في خيارات الطاقة، فلن يستيقظ الجهاز، لذا فإنّ ضبط «السماح بمؤقّتات الإيقاظ» والتحقّق الفعليّ من الاستيقاظ على الجهاز أمران ضروريّان. أمّا الكبح الدائم للسكون فيهدر الطاقة طوال تلك الفترة، ويُعدّ تصميماً سيّئ السلوك لأنّه يتجاوز إعدادات الطاقة التي حدّدها المستخدم.

الملف الشخصي للمؤلف

صفحة الملف الشخصي لمؤلف المقالة.

غو كومورا

مؤسّس شركة كومورا سوفت ذ.م.م.

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

روابط عامة

العودة إلى المدونة