SObjectizer عبارة عن إطار عمل C ++ 17 صغير نسبيًا يسمح لك باستخدام طرق مثل نموذج الممثل ونشر الاشتراك والاتصال بالعمليات المتسلسلة (CSP) في برامج C ++. مما يبسط إلى حد كبير تطوير تطبيقات معقدة متعددة الخيوط في C ++. إذا سمع القارئ عن SObjectizer للمرة الأولى ، فيمكنك أن تحدث انطباعًا عنه من هذا العرض التقديمي ، أو من هذا المقال القديم بالفعل.
بشكل عام ، لا يوجد الكثير من الأدوات المتشابهة المفتوحة والتي لا تزال قيد التطوير ولا تزال تعمل على تطوير C ++. يمكن للمرء أن يتذكر فقط QP / C ++ ، و CAF: C ++ Actor Framework ، والممثل-زيتا ، والمشروع الدوار الصغير جدًا. هناك خيار ، لكن ليس بهذا الحجم.
في الآونة الأخيرة ، أصبح هناك إصدار "رئيسي" آخر من SObjectizer متاحًا ، حيث ظهر أخيرًا شيء تم التحدث عنه منذ فترة طويلة ، والذي اتصلت به عدة مرات دون جدوى. يمكننا القول أنه قد تم الوصول إلى معلم رئيسي. هذه أيضًا مناسبة للحديث عن ما يتوقعه SObjectizer بعد إصدار الإصدار 5.7.0.
دعم Send_case في تحديد ()
لذا ، فإن أهم ابتكار ظهر في الإصدار v.5.7.0 والذي تم التوافق عليه مع الإصدار v.5.6 في العام الماضي (ونحن لا ننقطع التوافق) هو دعم send_case في وظيفة select (). ما الذي جعل اختيار SObjectizer () يشبه تحديد Go. الآن ، باستخدام select () ، لا يمكنك فقط قراءة الرسائل من العديد من قنوات CSP ، ولكن يمكنك أيضًا إرسال الرسائل الصادرة إلى تلك القنوات التي كانت جاهزة للكتابة.
ولكن من أجل الكشف عن هذا الموضوع ، عليك أن تبدأ من بعيد.
ظهور عناصر CSP في SObjectizer-5
ظهرت عناصر CSP ، وهي نظائرها في قنوات CSP ، في SObjectizer-5 ليس لتحديد مربع "دعم CSP" ، ولكن لحل مشكلة عملية.
كان الشيء أنه عندما يعتمد التطبيق بالكامل على SObjectizer ، فإن تبادل المعلومات بين مختلف الكيانات (أجزاء) من البرنامج تتحقق بالطريقة الواضحة الوحيدة. يتم تقديم كل شيء في التطبيق في شكل وكلاء (الجهات الفاعلة) والوكلاء ببساطة إرسال رسائل إلى بعضهم البعض بطريقة قياسية.
ولكن في التطبيق ، يتم تطبيق جزء فقط من الوظيفة على SObjectizer ...
على سبيل المثال ، تطبيق GUI على Qt أو wxWidgets ، يكون فيه الجزء الرئيسي من التعليمات البرمجية هو واجهة المستخدم الرسومية ، وهناك حاجة إلى SObjectizer لتنفيذ بعض مهام الخلفية. أو تتم كتابة جزء من التطبيق باستخدام مؤشرات ترابط عارية و Asio ، ويتم إرسال البيانات التي يقرأها Asio من الشبكة إلى وكلاء SObjectizer للمعالجة.
عندما يحتوي أحد التطبيقات على جزء SObjectizer وجزء غير SObjectizer ، فإن السؤال الذي يطرح نفسه هو: كيفية نقل المعلومات من جزء SObjectizer من التطبيق إلى الجزء غير SObjectizer؟
تم العثور على الحل في شكل ما يسمى سلاسل الرسائل (سلاسل) ، أي المحادثات. التي ، حدث ذلك تمامًا ، تحولت إلى جوهر قنوات CSP. يرسل الجزء SObjectizer من التطبيق رسائل إلى mchain بالطريقة المعتادة ، باستخدام وظيفة send () العادية.
لقراءة الرسائل من الجزء بخلاف SObjectizer ، يمكنك استخدام وظيفة الاستلام () الجديدة ، لاستخدامها والتي لم تكن بحاجة إليها لإنشاء عوامل أو الغوص في أي برمجيات أخرى من SObjectizer.
اتضح مخطط مفهومة تماما والعمل.
سوء استخدام mchains
علاوة على ذلك ، فقد تبين أن المخطط مفهوم للغاية وأنه يعمل بسرعة كبيرة ، حيث بدأت بعض التطبيقات على SObjectizer في الكتابة دون أي عوامل على الإطلاق ، فقط على mchain. أي باستخدام نهج CSP ، وليس نموذج الممثل. كان هناك بالفعل مقالات حول هذا الموضوع هنا على حبري: واحد واثنين .
هذا أدى إلى اثنين من عواقب مثيرة للاهتمام.
أولاً ، تجاوزت وظيفة التلقي () الميزات المتقدمة. كان هذا ضروريًا بحيث كان من الممكن إجراء مكالمة واحدة فقط لتلقي () ، والعودة التي ستحدث عندما يكون العمل الضروري قد تم بالفعل. فيما يلي أمثلة على ما يمكن أن يفعله SObjectizer ():
using namespace so_5;
ثانياً ، سرعان ما أصبح من الواضح أنه على الرغم من إمكانية وضع أنواع مختلفة من الرسائل في سلسلة سلاسل SObjectizer ، وعلى الرغم من وجود وظيفة استقبال () متقدمة ، فأنت تحتاج أحيانًا إلى أن تكون قادرًا على العمل مع عدة قنوات في وقت واحد ...
حدد () ولكن للقراءة فقط
تمت إضافة وظيفة select () إلى SObjectizer لقراءة الرسائل ومعالجتها من عدة سلاسل. لم يظهر خيار تحديد النشاط التجاري () تمامًا مثل ذلك ، ولكن تحت تأثير لغة Go. ولكن حدد SObjectizer () كان له ميزتين.
أولاً ، كان تحديدنا () ، مثل استقبال () ، موجهًا نصيًا ، عندما يتم تحديده () مرة واحدة فقط ويتم تنفيذ كل الأعمال المفيدة بداخله. على سبيل المثال:
using namespace so_5; mchain_t ch1 = env.create_mchain(...); mchain_t ch2 = env.create_mchain(...);
ثانياً ، حدد () لم يدعم إرسال الرسائل إلى القناة. أي كان من الممكن قراءة الرسائل من القنوات. ولكن لإرسال رسائل إلى القناة باستخدام select () - لا.
من الصعب الآن تذكر سبب حدوثه. ربما لأنه تبين أن تحديد () مع دعم send_case مهمة صعبة ولم يتم العثور على موارد لحلها.
mchain في SObjectizer أصعب من القنوات في Go
في البداية ، حدد () دون دعم send_case لا يعتبر مشكلة. الحقيقة هي أن mchains في SObjectizer لها تفاصيلها الخاصة التي لا تملكها قنوات Go.
أولاً ، تنقسم سلاسل SObjectizer إلى أبعاد وبسعة قصوى ثابتة. لذلك ، إذا تم تنفيذ send () لـ mchain بدون أبعاد ، فلن يتم حظر هذا send () من حيث المبدأ. لذلك ، لا معنى لاستخدام select () لإرسال رسالة إلى mchain بدون أبعاد.
ثانياً ، بالنسبة إلى السلاسل ذات السعة القصوى الثابتة ، عند الإنشاء ، فإنها تشير على الفور إلى ما يحدث عند محاولة كتابة رسالة إلى سلسلة كاملة:
- هل أحتاج إلى انتظار ظهور مساحة خالية في mchain. وإذا لزم الأمر ، كم من الوقت ؛
- إذا لم يكن هناك مساحة خالية ، فما عليك فعله: حذف أقدم رسالة من mchain ، وتجاهل الرسالة الجديدة ، ورمي استثناء أو حتى استدعاء std :: abort () (هذا النص الثابت مطلوب بشدة في الممارسة).
وبالتالي ، كان سيناريو متكرر إلى حد ما (حسب علمي) لاستخدام select في Go لإرسال رسالة لا يحظر goroutin بإحكام متاحًا فورًا في SObjectizer بدون شرارات ودون تحديد.
في النهاية ، حدد كامل ()
ومع ذلك ، فقد مر الوقت ، وأحيانًا كانت هناك حالات لا يزال يتأثر فيها عدم وجود دعم send_case في تحديد (). علاوة على ذلك ، في هذه الحالات ، لم تساعد القدرات المدمجة للوحات ، بل على العكس.
لذلك ، من وقت لآخر حاولت الاقتراب من مسألة تنفيذ send_case. ولكن حتى وقت قريب ، لا شيء يعمل. بشكل رئيسي لأنه لم يكن من الممكن التوصل إلى تصميم هذا send_case نفسه. أي كيف يجب أن تبدو send_case داخل select ()؟ ماذا يفعل بالضبط إذا كان من الممكن إرسال؟ في حالة الاستحالة؟ ما يجب القيام به مع التقسيم إلى أبعاد ثابتة وثابتة؟
كان من الممكن العثور على الإجابات التي جلس لي على هذه الأسئلة وغيرها فقط في ديسمبر 2019. يرجع إلى حد كبير إلى المشاورات مع الأشخاص الذين هم على دراية الذهاب واستخدموا اختيار الذهاب في العمل الحقيقي. حسنًا ، ما إن ظهرت صورة send_case أخيرًا ، فقد وصل التنفيذ إلى هناك.
حتى الآن يمكنك كتابة مثل هذا:
using namespace so_5; struct Greeting { std::string text_; }; select(from_all().handle_n(1), send_case(ch, message_holder_t<Greeting>::make("Hello!"), []{ std::cout << "Hello sent!" << std::endl; }));
الشيء المهم هو أن send_case في select () يتجاهل استجابة التحميل الزائد التي تم تعيينها لسلسلة المفاتيح المستهدفة. لذلك ، في المثال أعلاه ، يمكن إنشاء ch مع رد الفعل abort_app عند محاولة إرسال رسالة إلى القناة الكاملة. وإذا حاولت استدعاء simple send () للكتابة إلى ch ، فيمكنك استدعاء std :: abort (). ولكن في حالة تحديد () - وهذا لن يحدث ، حدد () سينتظر حتى تظهر مساحة خالية في الفصل. أو حتى يتم إغلاق الفصل.
في ما يلي بعض الأمثلة على ما يمكن أن يفعله send_case في تحديد SObjectizer ():
using namespace so_5;
بطبيعة الحال ، يمكن استخدام send_case في select () بالاقتران مع rece_case:
حتى الآن في SObjectizer ، يمكن استخدام نهج CSP ، كما يقولون ، في جميع الحقول. لن يكون أسوأ من في الذهاب. مطوّل ، بالطبع. ولكن ليس أسوأ :)
يمكننا القول أن التاريخ الطويل لإضافة دعم لنهج CSP إلى SObjectizer قد انتهى.
أشياء أخرى مهمة في هذا الإصدار
الانتقال النهائي إلى جيثب
SObjectizer عاش في الأصل وتطويرها في SourceForge . عام من الإعلانات التجارية منذ عام 2006. ولكن في SF.net ، كان أداء Subversion ينخفض وأقل ، لذلك في العام الماضي انتقلنا إلى BitBucket و Mercurial. بمجرد قيامنا بذلك ، أعلنت شركة Atlassian أن مستودعات Mercurial مع BitBucket سيتم حذفها بالكامل تمامًا. لذلك ، منذ أغسطس 2019 ، توجد كل من SObjectizer و so5extra على GitHub.
يحتوي SF.net على جميع المحتويات القديمة المتبقية ، بما في ذلك الويكي مع الوثائق الخاصة بالإصدارات السابقة من SObjectizer. وأيضًا قسم الملفات حيث يمكنك تنزيل أرشيفات من إصدارات مختلفة من SObjectizer / so5extra وليس فقط (على سبيل المثال ، ملفات PDF مع بعض العروض التقديمية حول SObjectizer ).
بشكل عام ، ابحث عنا الآن على جيثب . ولا تنسَ أن تضع النجوم ، فلدينا عدد قليل جدًا في الوقت الحالي ؛)
السلوك الثابت للرسائل المغلفة
في SO-5.7.0 ، حدث إصلاح صغير لم يكن بالإمكان ذكره. لكن الأمر يستحق القول ، لأن هذا دليل جيد على كيفية تأثير الميزات المختلفة المتراكمة في SObjectizer على بعضها البعض أثناء تطويرها.
منذ أربع سنوات ، تمت إضافة دعم الوكلاء ، وهم أجهزة الحالة الهرمية ، إلى SObjectizer (مزيد من التفاصيل هنا ). بعد ذلك ، بعد عامين آخرين ، تم إضافة مظاريف الرسائل إلى SObjectizer. أي عندما يتم إرسال الرسالة ، تم لفها في كائن مغلف إضافي ويمكن لهذا المغلف تلقي معلومات حول ما يحدث مع الرسالة.
واحدة من ميزات آلية الرسائل المغلفة هي أن المغلف على علم بأن الرسالة قد تم تسليمها إلى المرسل إليه. أي أنه تم العثور على معالج لهذه الرسالة في وكيل المشترك وأنه تم استدعاء هذا المعالج.
اتضح أنه إذا كان وكيل المستلم للرسالة عبارة عن جهاز حالة هرمي يستخدم ميزة مثل suppress()
(أي فرض الرسالة على تجاهلها في حالة معينة) ، فقد يتلقى المغلف إشعارًا تسليمًا غير صحيح ، على الرغم من أن الرسالة قد تم رفضها بالفعل بسبب suppress()
. كان الموقف أكثر إثارة للاهتمام مع transfer_to_state()
، لأنه بعد تغيير حالة الوكيل المستلم ، يمكن العثور على معالج الرسائل ، أو قد يكون غائباً. لكن تم إبلاغ المغلف حول تسليم الرسالة على أي حال.
حالات نادرة جدًا ، حسب علمي ، لم يتم عرضها من قبل أي شخص. ومع ذلك ، تم إجراء سوء تقدير.
لذلك ، في SO-5.7.0 تم تحسين هذه النقطة وإذا تم تجاهل الرسالة كنتيجة لتطبيق suppress()
أو transfer_to_state()
، فإن الظرف لم يعد يعتقد أن الرسالة قد تم تسليمها إلى المرسل إليه.
في عام 2017 ، بدأنا في إنشاء مكتبة بمكونات إضافية لـ SObjectizer تسمى so5extra . خلال هذا الوقت ، نمت المكتبة بشكل كبير وتحتوي على العديد من الأشياء المفيدة في الأسرة.
تم توزيع So5extra في الأصل بموجب ترخيص مزدوج: GNU Affero GPL v.3 للمشاريع مفتوحة المصدر والتجارية للمشروعات المغلقة.
الآن قمنا بتغيير ترخيص so5extra ونبدأ من الإصدار 1.4.0 يتم توزيع so5extra تحت رخصة BSD-3-CLAUSE. أي يمكن استخدامه مجانًا حتى عند تطوير البرامج الاحتكارية.
لذلك ، إذا كنت تفتقد شيئًا ما في SObjectizer ، يمكنك إلقاء نظرة على so5extra ، ماذا لو كان لديك بالفعل ما تحتاجه؟
مستقبل SObjectizer
قبل أن تقول بضع كلمات حول ما ينتظره SObjectizer ، تحتاج إلى جعل استطرادا هاما. خاصة بالنسبة لأولئك الذين يعتقدون أن SObjectizer هو "مضيعة مرجعية" ، "صنعة عالية في الركبة" ، "مختبر للطالب" ، "إسقاط تجريبي يتخلى عنه المؤلفون عندما يلعبون بما فيه الكفاية" ... (هذا ليس سوى جزء من الخصائص التي سمعناها من الخبراء في من الإنترنت لدينا على مدى 4-5 سنوات الماضية).
لقد تم تطوير SObjectizer منذ ما يقرب من ثمانية عشر عاماً. وأستطيع أن أقول بمسؤولية أنه لم يكن يومًا مشروعًا رائدًا. هذه أداة عملية دخلت في عمل حقيقي منذ أول إصدار لها في العام 2002.
لقد اقتنعت أنا وزملائي والأشخاص الذين تجرأوا على تجربة SObjectizer وتجربتها عدة مرات أن SObjectizer يجعل تطوير بعض أنواع تطبيقات C ++ ذات مؤشرات الترابط المتعددة أسهل كثيرًا. بالطبع ، SObjectizer ليس رصاصة فضية ، ولا يمكن استخدامه دائمًا بأي حال من الأحوال. ولكن حيثما ينطبق ذلك ، فإنه يساعد.
توفر الحياة بانتظام فرصة مرة أخرى للإقناع بهذا. من وقت لآخر ، تندرج الشفرة المتعددة مؤشرات ترابط شخص آخر في انتباهنا ، حيث لم يكن هناك شيء مشابه لبرنامج SObjectizer ومن غير المرجح أن يظهر على الإطلاق. تعامل مع هذا الرمز هنا وهناك ، هناك لحظات مذهلة عندما يؤدي استخدام الجهات الفاعلة أو قنوات CSP إلى جعل الشفرة أكثر بساطة وموثوقية. ولكن لا ، يجب عليك إنشاء أنماط غير تافهة من تفاعل مؤشر الترابط من خلال mutex-s و condition_variables حيث يمكنك في SObjectizer أن تديرها باستخدام سلسلة واحدة واحدة ، ورسالتين من الرسائل وجهاز توقيت مدمج في SObjectizer. ثم تقضي أيضًا الكثير من الوقت لاختبار هذه المخططات غير التافهة ...
لذا SObjectizer كان مفيدًا لنا. أجرؤ على الاعتقاد بأنه كان مفيدًا ليس فقط لنا. والأهم من ذلك ، لقد كان هنا منذ فترة طويلة ومتاح للجميع بحرية. لن يترك أي مكان. وأين تذهب إلى ما هو في OpenSource بموجب ترخيص متساهل؟ ؛)
شيء آخر هو أننا قمنا بتطبيق جميع قائمة الأمنيات الكبيرة لدينا في SObjectizer. وسيتم تحديد التطوير المستقبلي لـ SObjectizer ليس باحتياجاتنا بقدر رغبات المستخدمين.
ستكون هناك مثل هذه الرغبات - ستكون هناك ميزات جديدة في SObjectizer.
لن يكون الأمر كذلك ... حسنًا ، سنصدر فقط الإصدارات التصحيحية من وقت لآخر ونفحص أداء SObjectizer ضمن الإصدارات الجديدة من برامج التحويل البرمجي C ++.
إذا أردت رؤية شيء ما في SObjectizer ، فأخبرنا بذلك. إذا كنت بحاجة إلى أي مساعدة مع SObjectizer ، فلا تتردد في الاتصال بنا (من خلال Issues on GitHub أو Google group ) ، وسوف نحاول بالتأكيد تقديم المساعدة.
حسنًا ، أود أن أشكر هؤلاء القراء الذين تمكنوا من القراءة حتى نهاية هذا المقال. وسأحاول الإجابة على أي أسئلة حول SObjectizer / so5extra ، في حالة حدوث ذلك.
PS. سأكون ممتنًا إذا كان القراء سيجدون وقتًا لكتابة التعليقات إذا كان من الممتع / المفيد قراءة مقالات حول SObjectizer وما إذا كانوا يريدون القيام بذلك في المستقبل. أم أنه من الأفضل لنا أن نتوقف عن إضاعة الوقت في كتابة مثل هذه المقالات ، وبالتالي التوقف عن قضاء وقت في مستخدمي Habr؟
PPS. أو ربما شخص ما اعتبر SObjectizer كأداة لا يمكن تطبيقها لسبب أو لآخر؟ سيكون من المثير للاهتمام معرفة هذا.