سؤال

قادتني الوثائق الملتوية إلى الاعتقاد بأنه من الجيد الجمع بين تقنيات مثل reactor.spawnProcess() و threads.deferToThread() وفي نفس التطبيق، فإن المفاعل سيتعامل مع هذا الأمر بأناقة تحت الأغطية.عند تجربتها بالفعل، وجدت أن طلبي وصل إلى طريق مسدود.باستخدام سلاسل رسائل متعددة بمفردها، أو العمليات الفرعية بنفسها، كل شيء على ما يرام.

وبالنظر إلى مصدر المفاعل، أجد أن SelectReactor.spawnProcess() طريقة تدعو ببساطة os.fork() دون أي اعتبار لخيوط متعددة قد تكون قيد التشغيل.وهذا ما يفسر الجمود، لأنه يبدأ بالدعوة إلى os.fork() سيكون لديك عمليتان مع عدة سلاسل رسائل متزامنة تعمل وتفعل من يعرف ماذا باستخدام نفس واصفات الملفات.

سؤالي لـ SO هو، ما هي أفضل استراتيجية لحل هذه المشكلة؟

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

هل قام أحد بهذا من قبل؟أهناك طريق أسرع؟

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

المحلول 3

وبالعودة إلى هذه المشكلة بعد مرور بعض الوقت، وجدت أنه إذا قمت بما يلي:

reactor.callFromThread(reactor.spawnProcess, *spawnargs)

بدلا من هذا:

reactor.spawnProcess(*spawnargs)

ثم تختفي المشكلة في حالة الاختبار الصغيرة الخاصة بي.هناك ملاحظة في الوثائق الملتوية "استخدام العمليات" دفعتني إلى تجربة ذلك:"معظم التعليمات البرمجية في Twisted ليست آمنة لمؤشر الترابط.على سبيل المثال، كتابة البيانات إلى وسيلة نقل من بروتوكول ليست آمنة لمؤشر الترابط."

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

نصائح أخرى

ما هي أفضل استراتيجية لحل هذه المشكلة؟

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

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

قد يكون الحل البديل (والذي قد يكون أكثر اختراقًا إلى حد ما، ولكنه قد يواجه أيضًا تحديات تنفيذ أقل) هو الاتصال sys.setcheckinterval مع عدد كبير جدا قبل الاتصال os.fork, ، ثم قم باستعادة الفاصل الزمني للتحقق الأصلي في العملية الأصلية فقط.يجب أن يكون هذا كافيًا لتجنب أي تبديل لمؤشر الترابط في العملية حتى os.execvpe يحدث، وتدمير كافة المواضيع الإضافية.هذا ليس صحيحًا تمامًا، لأنه سيترك موارد معينة (مثل كائنات المزامنة والشروط) في حالة سيئة، لكنك تستخدمها مع deferToThread ليس شائعًا جدًا، لذا ربما لا يؤثر ذلك على حالتك.

النصيحة التي يقدمها جان بول في إجابته جيدة، لكن هذا يجب العمل (ويفعل في معظم الحالات).

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

ثانية، fork() لا يقوم بإنشاء سلاسل رسائل متعددة في العملية الفرعية. وفقا لمعيار الوصف fork(),

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

الآن، هذا لا يعني أن هناك لا مشاكل تعدد المحتملة مع spawnProcess;يقول المعيار أيضًا:

...لتجنب الأخطاء، لا يجوز للعملية الفرعية سوى تنفيذ عمليات آمنة للإشارة غير المتزامنة حتى يتم استدعاء إحدى وظائف exec ...

ولا أعتقد أن هناك أي شيء يضمن استخدام العمليات الآمنة للإشارة غير المتزامنة فقط.

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

من المؤكد أن fork() على Linux يترك العملية الفرعية بخيط واحد فقط.

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

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