جدولة Coroutine المتزامنة

بعد تعلم قناة وظيفة الاتصال في coroutine ، تحدثنا بعد ذلك عن وظيفة WaitGroup. في الواقع ، هي نفسها أداة جدولة coroutine. لن ندخل في تفاصيل حول وظيفتها ، فبعد كل شيء ، أوضح المقال السابق ذلك تمامًا. اليوم ، سنستمر في موضوع WaitGroup وسنواصل الحديث عن الجدولة المتزامنة لـ coroutines.

تنفيذ Coroutine وحاويات coroutine

بعد تعلم هذا ، لا أعرف ما إذا كنت قد وجدت مشكلة ، أي إذا لم تكن في حاوية coroutine ، وإذا تمت مصادفة عملية حظر ، فسيتم تنفيذ coroutine بالتتابع. وإذا كان في حاوية coroutine ، فإنه يصبح تنفيذًا متزامنًا.

go(function(){
   sleep(2);
   echo "cid1:" . Co::getCid() , PHP_EOL;
});
go(function(){
   sleep(1);
   echo "cid2:" . Co::getCid() , PHP_EOL;
});
//cid1:1
//cid2:2

في كود الاختبار أعلاه ، يستريح coroutine الأول لمدة ثانيتين ، ويستريح الثاني لمدة ثانية واحدة ، ويتم تنفيذ نتائج الإخراج النهائية بالتتابع. بعد ذلك نضعه في حاوية coroutine.

\Swoole\Coroutine\run(function(){
  go(function(){
      sleep(2);
      echo "cid1:" . Co::getCid() , PHP_EOL;
  });
  go(function(){
      sleep(1);
      echo "cid2:" . Co::getCid() , PHP_EOL;
  });
});
//cid2:3
//cid1:2

في حاوية coroutine ، ينفذ coroutine الثاني أولاً ويخرج المحتوى. من الواضح أن هذه حالة تشبه إلى حد بعيد العملية المتوازية. فهل هذا هو الحال في الواقع؟

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

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

تقوم حاوية coroutine في الواقع بتنفيذ مجموعة من بيئة تنفيذ coroutine الداخلية ، بحيث يمكن أن تكون العديد من الرموز التي تم تنفيذها في الأصل بشكل متزامن غير متزامنة في الحاوية. يمكننا أيضًا استخدام co :: sleep () خارج الحاوية لتعليق العمليات المتزامنة ، ولكن داخل الحاوية ، يمكن تفعيل sleep () مباشرة. بالإضافة إلى ذلك ، في Swoole ، النوم () ليس شيئًا جيدًا ، فقد نستخدمه كثيرًا للتوضيح ، ولكن في سيناريوهات تطوير الأعمال الحقيقية ، من الأفضل عدم استخدامه. للسبب ، يرجى الرجوع إلى Swoole Programming Notes https://wiki.swoole.com/#/getting_started/notice؟id=sleepusleep Impact  .

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

جدولة أبسط من WaitGroup

نظرًا لاستخدام coroutines ، نحتاج بالتأكيد إلى إمكانات التزامن ، وقد تؤدي العمليات المتزامنة إلى حدوث بعض المشكلات. في الواقع ، هو نتيجة التنفيذ المتزامن للعديد من الأعمال التي تحدثنا عنها في المرة السابقة. في ذلك الوقت ، استخدمنا WaitGroup لتحقيق تأثير انتظار coroutines متعددة لإكمال الإرجاع المتزامن. ولكن في Swoole ، يتم أيضًا توفير أداة أبسط تسمى Barrier.

\Swoole\Coroutine\run(function () {
   $time = microtime(true);

   $barrier = \Swoole\Coroutine\Barrier::make();

   foreach (range(14as $i) {
       go(function () use ($barrier, $i) {
           \Swoole\Coroutine\System::sleep($i);
       });
   }

   \Swoole\Coroutine\Barrier::wait($barrier);

   echo microtime(true) - $time, PHP_EOL;
});
// 4.0022649765015

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

هل يبدو الأمر أكثر ملاءمة من WaitGroup؟ إذا كان هناك العديد من coroutines ، فيمكنك كتابة أساليب add () و done () أقل بكثير.

تطبيق Coroutine والجدولة على خادم غير متزامن

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

$serv = new Swoole\Http\Server("0.0.0.0"9501, SWOOLE_PROCESS);

$serv->on('request'function ($req, $resp) {
    $time = microtime(true);
    $wg = new \Swoole\Coroutine\WaitGroup();

    $wg->add();
    $wg->add();

    $res = 1;

    go(function () use ($wg, &$res) {
        co::sleep(3);
        $res += 1;
        $wg->done();
    });

    go(function () use ($wg, &$res) {
        co::sleep(4);
        $res *= 10;
        $wg->done();
    });

    $wg->wait();

    $endTime = microtime(true) - $time;
    $resp->end($res . " - " . $endTime);
});
$serv->start();

//20 - 4.002151966095

في الفقرة أعلاه ، نحن ننتظر بشكل متزامن من خلال WaitGroup. يمكنك محاولة استبداله بـ Barrier لرؤية التأثير.

لخص

محتوى اليوم بسيط نسبيًا ، بعد كل شيء ، بعد أن أصبح لدينا الأساس السابق ، سيصبح من الصعب علينا تعلم وفهم المفاهيم الجديدة أقل وأقل. أهم شيء هو فهم مفهوم التنفيذ غير المتزامن والعودة المتزامنة ، وفهم سبب القيام بذلك. يمكن البحث في هذه المعلومات الإضافية عن المحتوى المتعلق بـ Promise في JS المذكور سابقًا.

كود الاختبار:

https://github.com/zhangyue0503/swoole/blob/main/4.Swoole٪E5٪8D٪8F٪E7٪A8٪8B/source/4.5٪E5٪8D٪8F٪E7٪A8٪8B٪E5٪ B9٪ B6٪ E5٪ 8F٪ 91٪ E8٪ B0٪ 83٪ E5٪ BA٪ A6.php

الوثائق المرجعية:

https://wiki.swoole.com/#/coroutine/barrier

https://wiki.swoole.com/#/coroutine/multi_call؟