سؤال

في الكود الخاص بي ، أستخدم QueueUserAPC ل مقاطعة الخيط الرئيسي من عمله الحالي من أجل استدعاء بعض رد الاتصال أولاً قبل العودة إلى عمله السابق.

std::string buffer;
std::tr1::shared_ptr<void> hMainThread;
VOID CALLBACK myCallback (ULONG_PTR dwParam) {
    FILE * f = fopen("somefile", "a");
    fprintf(f, "CALLBACK WAS INVOKED!\n");
    fclose(f);
}
void AdditionalThread () {
    // download some file using synchronous wininet and store the
    // HTTP response in buffer
    QueueUserAPC(myCallback, hMainThread.get(), (ULONG_PTR)0);
}
void storeHandle () {
    HANDLE hUnsafe;
    DuplicateHandle(GetCurrentProcess(), GetCurrentThread(), 
        GetCurrentProcess(), &hUnsafe, 0, FALSE, DUPLICATE_SAME_ACCESS);
    hMainThread.reset(hUnsafe, CloseHandle);
}
void startSecondThread () {
    CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)AdditionalThread, 0, 0, NULL);
}

storeHandle و startSecondThread تتعرض لمترجم LUA الذي يعمل في الخيط الرئيسي مع أشياء أخرى. ما أفعله الآن ، هو

  1. يستحضر storeHandle من مترجم لوا الخاص بي. DuplicateHandle إرجاع قيمة غير صفرية وبالتالي تنجح.
  2. يستحضر startSecondThread من مترجم لوا الخاص بي. يبدأ الموضوع الإضافي بشكل صحيح ، و QueueUserAPC إرجاع قيمة غير صفرية ، قائلة ، سارت الأمور على ما يرام.
  3. بقدر ما فهمت QueueUserAPC, myCallback يجب الآن استدعاء من الموضوع الرئيسي. ومع ذلك ، لا.

لو QueueUserAPC هي الطريقة الصحيحة لتحقيق هدفي (==> انظر الآخر سؤال):

  • كيف يمكنني الحصول على هذا العمل؟

إذا كان ينبغي عليّ طريقة أخرى لمقاطعة الموضوع الرئيسي:

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

المحلول

نعم ، QueueUserAPC ليس هو الحل هنا. سيتم تشغيل رد الاتصال الخاص به فقط عند كتل مؤشر الترابط ويسمح المبرمج صراحةً بالانتظار ليكون قابلاً للتنبيه. هذا غير مرجح.

أتردد في نشر الحل لأنه سيؤدي إلى مشكلة هائلة. يمكنك تنفيذ مقاطعة مؤشر ترابط مع SuperDthread () و getThreadContext () و SetThreadContext () و ResumeThread (). المفتاح هو حفظ قيمة السياق.

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

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

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

نصائح أخرى

من ما يمكنني فهمه في MSDN, ، لا يتم استدعاء رد الاتصال حتى يدخل الخيط في حالة قابلة للتنبيه ، ويتم ذلك عن طريق الاتصال SleepEx, SignalObjectAndWait, WaitForSingleObjectEx, WaitForMultipleObjectsEx, ، أو MsgWaitForMultipleObjectsEx.

لذلك إذا كنت لا ترغب حقًا في إجراء بعض الاقتراع ، فلا أعتقد أن هذه الطريقة تتكيف مع قضيتك.

هل من الممكن تنفيذ "مضخة الرسائل" (أو بالأحرى مستمع للحدث) في موضوعك الرئيسي وتفويض جميع أعمالها الحالية إلى موضوع آخر؟ في هذه الحالة ، ينتظر الخيط الرئيسي أي حدث يتم تعيينه بواسطة المواضيع الأخرى.

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