it-swarm.asia

لماذا يجب علي استخدام std :: async؟

أحاول استكشاف جميع خيارات المعيار C++ 11 الجديد بتعمق ، أثناء استخدام std :: async وقراءة تعريفه ، لاحظت شيئين ، على الأقل في نظام التشغيل Linux مع gcc 4.8.1:

  • يطلق عليه async ، لكنه حصل بالفعل على "سلوك متسلسل" ، وأساسًا في الصف حيث يمكنك استدعاء المستقبل المرتبط بوظيفة async foo ، كتل البرنامج حتى تنفيذ فو اكتملت.
  • يعتمد ذلك على المكتبة الخارجية نفسها تمامًا مثل المكتبة الأخرى ، وعلى الحلول الأفضل غير المحظورة ، وهو ما يعني pthread ، إذا كنت تريد استخدام std::async فأنت بحاجة إلى pthread.

في هذه المرحلة ، من الطبيعي بالنسبة لي أن أسأل عن سبب اختيار std :: async على مجموعة بسيطة من functor؟ إنه حل لا يتعدى حجمه على الإطلاق ، فكلما اتصلت به في المستقبل ، كلما كان استجابة البرنامج أقل استجابة.

هل فاتني شيء ؟ هل يمكنك عرض مثال تم منحه للتنفيذ بطريقة غير متزامنة وغير محظورة؟

61
user2485710

إذا كنت بحاجة إلى نتيجة لعملية غير متزامنة ، فأنت لديك لحظر ، بغض النظر عن المكتبة التي تستخدمها. الفكرة هي أنه يجب عليك اختيار وقت الحظر ، ونأمل عندما تقوم بذلك ، أن تمنعه ​​لوقت لا يكاد يذكر ، لأن كل العمل قد تم بالفعل.

لاحظ أيضًا أنه يمكن تشغيل std::async مع سياسات std::launch::async أو std::launch::deferred. إذا لم تحدده ، فسيُسمح للتطبيق بالاختيار ، ويمكنه اختيار استخدام التقييم المؤجل ، مما قد ينتج عنه كل العمل الذي تقوم به عندما تحاول الحصول على النتيجة من المستقبل ، مما يؤدي إلى كتلة أطول . لذلك إذا كنت تريد التأكد من أن العمل قد تم بشكل غير متزامن ، فاستخدم std::launch::async.

53
juanchopanza
  • يطلق عليه غير متزامن ، لكنه حصل على "سلوك متسلسل" حقًا ،

لا ، إذا استخدمت سياسة std::launch::async ، فسيتم تشغيلها بشكل غير متزامن في سلسلة رسائل جديدة. إذا لم تحدد سياسة ، فسيتم تشغيلها {may في سلسلة رسائل جديدة.

في الصف الذي تقوم فيه بالاتصال بالمستقبل المرتبط بوظيفة async الخاصة بك ، سيتم حظر البرنامج حتى يكتمل تنفيذ foo.

يتم حظره فقط إذا لم يتم إكمال foo ، ولكن إذا تم تشغيله بشكل غير متزامن (على سبيل المثال لأنك تستخدم سياسة std::launch::async) فربما يكون قد تم إكماله قبل الحاجة إليه.

  • يعتمد ذلك على المكتبة الخارجية نفسها تمامًا مثل المكتبات الأخرى ، والحلول الأفضل غير المحظورة ، وهو ما يعني pthread ، إذا كنت تريد استخدام std :: async ، فأنت بحاجة إلى pthread.

خطأ ، ليس من الضروري أن يتم تنفيذه باستخدام Pthreads (وعلى Windows ليس كذلك ، فإنه يستخدم ميزات ConcRT.)

في هذه المرحلة ، من الطبيعي بالنسبة لي أن أسأل عن سبب اختيار std :: async على مجموعة بسيطة من functor؟

لأنه يضمن سلامة الخيط وينشر الاستثناءات عبر المواضيع. يمكنك أن تفعل ذلك مع مجموعة بسيطة من الجنائز؟

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

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

الآن ، صحيح أنه مع تطبيق مجلس التعاون الخليجي ، إذا لم تقدم سياسة إطلاق ، فلن يتم تشغيل الإصدارات الحالية في خيط جديد (هناك تقرير bugzilla لذلك) ، ولكن هذه خاصية لهذا التطبيق ، ليس من std::async بشكل عام. يجب ألا تخلط بين المواصفات في المعيار وتطبيق معين. تعد قراءة تطبيق مكتبة قياسية واحدة طريقة سيئة للتعرف على الإصدار C++ 11.

هل يمكنك عرض مثال تم منحه للتنفيذ بطريقة غير متزامنة وغير محظورة؟

هذا لا ينبغي أن يمنع:

auto fut = std::async(std::launch::async, doSomethingThatTakesTenSeconds);
auto result1 = doSomethingThatTakesTwentySeconds();
auto result2 = fut.get();

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

74
Jonathan Wakely

أظن أن مشكلتك تكمن في std::future أن تقول إنها تمنع get. انها كتل فقط إذا كانت النتيجة ليست جاهزة بالفعل .

إذا كنت تستطيع الترتيب لتكون النتيجة جاهزة بالفعل ، فهذه ليست مشكلة.

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

أو ، يمكنك إنهاء جميع الأعمال التي يمكنك القيام بها محليًا ، ثم حظر العمل عن بُعد.

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

تتصرف هذه المهمة كمهمة خطية لأولئك الذين يتفاعلون معها من الخارج - عند الانتهاء ، يتم فرز المصفوفة.

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

13
Yakk - Adam Nevraumont

في المرجع : http://en.cppreference.com/w/cpp/thread/async

إذا تم تعيين علامة async (على سبيل المثال ، السياسة & std :: launch :: async! = 0) ، فسينفذ async الوظيفة f في سلسلة عمليات منفصلة للتنفيذ كما لو كانت ناتجة عن std :: thread (f ، args ...) ، باستثناء ذلك إذا كانت الدالة f تُرجع قيمة أو ترمي استثناءً ، فسيتم تخزينها في الحالة المشتركة التي يمكن الوصول إليها من خلال std :: future التي تعود غير المتزامن إلى المتصل .

إنها خاصية لطيفة للاحتفاظ بسجل للاستثناءات التي تم طرحها.

3
fatihk

http://www.cplusplus.com/reference/future/async/

هناك ثلاثة أنواع من السياسة ،

  1. launch::async
  2. launch::deferred
  3. launch::async|launch::deferred

بشكل افتراضي ، يتم تمرير launch::async|launch::deferred إلى std::async.

0
Masoud Bolhassani