توفر هذه المكتبة غلافًا صغيرًا وسهلًا حول ملحق PCNTL الخاص بـ PHP. فهو يسمح بتشغيل عمليات مختلفة بالتوازي، مع واجهة برمجة التطبيقات (API) سهلة الاستخدام.
نحن نستثمر الكثير من الموارد في إنشاء أفضل الحزم مفتوحة المصدر في فئتها. يمكنك دعمنا عن طريق شراء أحد منتجاتنا المدفوعة.
نحن نقدر بشدة إرسالك لنا بطاقة بريدية من مسقط رأسك، مع ذكر الحزمة (الحزم) التي تستخدمها. ستجد عنواننا على صفحة الاتصال لدينا. ننشر جميع البطاقات البريدية المستلمة على جدار البطاقات البريدية الافتراضية لدينا.
يمكنك تثبيت الحزمة عبر الملحن:
composer require spatie/async
use Spatie Async Pool ;
$ pool = Pool:: create ();
foreach ( $ things as $ thing ) {
$ pool -> add ( function () use ( $ thing ) {
/ / Do a thing
})-> then ( function ( $ output ) {
/ / Handle success
})-> catch ( function ( Throwable $ exception ) {
/ / Handle exception
});
}
$ pool -> wait ();
عند إنشاء عمليات غير متزامنة، ستحصل على مثيل ParallelProcess
الذي تم إرجاعه. يمكنك إضافة خطافات الأحداث التالية إلى إحدى العمليات.
$ pool
-> add ( function () {
/ / ...
})
-> then ( function ( $ output ) {
/ / On success , `$output` is returned by the process or callable you passed to the queue .
})
-> catch ( function ( $ exception ) {
/ / When an exception is thrown from within a process , it 's caught and passed here.
})
-> timeout ( function () {
/ / A process took too long to finish.
})
;
بدلًا من استخدام التوابع في الكائن $pool
، يمكنك أيضًا استخدام الدالتين المساعدتين async
await
.
use Spatie Async Pool ;
$ pool = Pool:: create ();
foreach ( range ( 1 , 5 ) as $ i ) {
$ pool [] = async ( function () {
usleep ( random_int ( 10 , 1000 ));
return 2 ;
})-> then ( function ( int $ output ) {
$ this -> counter += $ output ;
});
}
await ( $ pool );
إذا تم طرح Exception
أو Error
من داخل عملية فرعية، فيمكن اكتشافه لكل عملية عن طريق تحديد رد اتصال في طريقة ->catch()
.
$ pool
-> add ( function () {
/ / ...
})
-> catch ( function ( $ exception ) {
/ / Handle the thrown exception for this child process.
})
;
إذا لم تتم إضافة معالج أخطاء، فسيتم طرح الخطأ في العملية الأصلية عند استدعاء await()
أو $pool->wait()
.
إذا توقفت العملية الفرعية بشكل غير متوقع دون رمي Throwable
، فسيتم تغليف الإخراج المكتوب إلى stderr
وإلقائه كـ SpatieAsyncParallelError
في العملية الأصلية.
من خلال كتابة تلميحات لوظائف catch
، يمكنك توفير معالجات أخطاء متعددة، كل منها لأنواع الأخطاء الفردية.
$ pool
-> add ( function () {
throw new MyException ( ' test ' );
})
-> catch ( function ( MyException $ e ) {
/ / Handle `MyException`
})
-> catch ( function ( OtherException $ e ) {
/ / Handle `OtherException`
});
لاحظ أنه بمجرد معالجة الاستثناء، فإنه لن يؤدي إلى تشغيل أي معالجات أخرى
$ pool
-> add ( function () {
throw new MyException ( ' test ' );
})
-> catch ( function ( MyException $ e ) {
/ / This one is triggerd when `MyException` is thrown
})
-> catch ( function ( Exception $ e ) {
/ / This one is not triggerd , even though `M yException ` extends `E xception `
});
إذا كنت بحاجة إلى إيقاف التجمع مبكرًا، لأن المهمة التي كان يؤديها قد اكتملت بواسطة إحدى العمليات الفرعية، فيمكنك استخدام الأسلوب $pool->stop()
. سيؤدي هذا إلى منع التجمع من بدء أية عمليات إضافية.
use Spatie Async Pool ;
$ pool = Pool:: create ();
/ / Generate 10 k processes generating random numbers
for ( $ i = 0 ; $ i < 10000 ; $ i ++) {
$ pool -> add ( function () use ( $ i ) {
return rand ( 0 , 100 );
})-> then ( function ( $ output ) use ( $ pool ) {
/ / If one of them randomly picks 100 , end the pool early .
if ( $ output === 100 ) {
$ pool -> stop ();
}
});
}
$ pool -> wait ();
لاحظ أن التجمع سيصبح عديم الفائدة بعد إيقافه، ويجب إنشاء تجمع جديد إذا لزم الأمر.
افتراضيًا، سيستخدم المجمع php
لتنفيذ عملياته الفرعية. يمكنك تكوين ثنائي آخر مثل ذلك:
Pool:: create ()
-> withBinary ( ' /path/to/php ' );
إلى جانب استخدام عمليات الإغلاق، يمكنك أيضًا العمل مع Task
. تكون Task
مفيدة في المواقف التي تحتاج فيها إلى المزيد من أعمال الإعداد في العملية الفرعية. نظرًا لأن العملية الفرعية يتم تشغيلها دائمًا من لا شيء، فمن المحتمل أنك سترغب في التهيئة على سبيل المثال. حاوية التبعية قبل تنفيذ المهمة. فئة Task
تسهل القيام بذلك.
use Spatie Async Task ;
class MyTask extends Task
{
public function configure ()
{
/ / Setup eg . dependency container , load config , ...
}
public function run ()
{
/ / Do the real work here.
}
}
/ / Add the task to the pool
$ pool -> add ( new MyTask ());
إذا كنت تريد تغليف منطق مهمتك، ولكنك لا ترغب في إنشاء كائن Task
كامل، فيمكنك أيضًا تمرير كائن قابل للاستدعاء إلى Pool
.
class InvokableClass
{
/ / ...
public function __invoke ()
{
/ / ...
}
}
$ pool -> add ( new InvokableClass ( / * ... * / ));
أنت حر في إنشاء أي عدد تريده من التجمعات، ولكل تجمع قائمة انتظار خاصة به من العمليات التي سيتعامل معها.
يمكن تكوين التجمع بواسطة المطور:
use Spatie Async Pool ;
$ pool = Pool:: create ()
/ / The maximum amount of processes which can run simultaneously.
-> concurrency ( 20 )
/ / The maximum amount of time a process may take to finish in seconds
/ / ( decimal places are supported for more granular timeouts ) .
-> timeout ( 15 )
/ / Configure which autoloader sub processes should use.
-> autoload ( __DIR__ . ' /../../vendor/autoload.php ' )
/ / Configure how long the loop should sleep before re - checking the process statuses in microseconds .
-> sleepTime ( 50000 )
;
إذا لم يتم تثبيت الملحقات المطلوبة ( pcntl
و posix
) في وقت تشغيل PHP الحالي لديك، فسيعود Pool
تلقائيًا إلى التنفيذ المتزامن للمهام.
تحتوي فئة Pool
على طريقة ثابتة isSupported
يمكنك الاتصال بها للتحقق مما إذا كان النظام الأساسي الخاص بك قادرًا على تشغيل العمليات غير المتزامنة.
إذا كنت تستخدم Task
لتشغيل العمليات، فسيتم استدعاء طريقة run
هذه المهام فقط عند التشغيل في الوضع المتزامن.
عند استخدام هذه الحزمة، ربما تتساءل عما يحدث تحت السطح.
نحن نستخدم مكون symfony/process
لإنشاء وإدارة العمليات الفرعية في PHP. من خلال إنشاء عمليات فرعية بسرعة، نحن قادرون على تنفيذ نصوص PHP بالتوازي. يمكن لهذا التوازي تحسين الأداء بشكل ملحوظ عند التعامل مع مهام متزامنة متعددة، والتي لا تحتاج حقًا إلى انتظار بعضها البعض. من خلال منح هذه المهام عملية منفصلة للتشغيل، يمكن لنظام التشغيل الأساسي الاهتمام بتشغيلها بالتوازي.
هناك تحذير عند إنشاء العمليات ديناميكيًا: تحتاج إلى التأكد من عدم وجود عدد كبير جدًا من العمليات في وقت واحد، وإلا قد يتعطل التطبيق. تهتم فئة Pool
التي توفرها هذه الحزمة بمعالجة أي عدد تريده من العمليات عن طريق جدولتها وتشغيلها عندما يكون ذلك ممكنًا.
هذا هو الجزء الذي يقوم به async()
أو $pool->add()
. الآن دعونا نلقي نظرة على ما يفعله await()
أو $pool->wait()
.
عندما يتم إنشاء عمليات متعددة، يمكن أن يكون لكل منها وقت منفصل حتى الانتهاء. قد تكون عملية واحدة على سبيل المثال. يجب عليك انتظار مكالمة HTTP، بينما يتعين على الآخر معالجة كميات كبيرة من البيانات. في بعض الأحيان يكون لديك أيضًا نقاط في التعليمات البرمجية الخاصة بك والتي يجب أن تنتظر حتى يتم إرجاع نتيجة العملية.
ولهذا السبب يتعين علينا الانتظار عند نقطة زمنية معينة: حتى تنتهي جميع العمليات الموجودة على المجموعة، حتى نتمكن من التأكد من أنه من الآمن الاستمرار دون قتل العمليات الفرعية التي لم يتم تنفيذها بعد عن طريق الخطأ.
يتم انتظار جميع العمليات باستخدام حلقة while
، والتي ستنتظر حتى تنتهي جميع العمليات. يتم تحديد وقت انتهاء العملية باستخدام مستمع على إشارة SIGCHLD
. تنبعث هذه الإشارة عند انتهاء عملية فرعية بواسطة نواة نظام التشغيل. اعتبارًا من PHP 7.1، أصبح هناك دعم أفضل بكثير للاستماع إلى الإشارات والتعامل معها، مما يجعل هذا النهج أكثر أداءً من على سبيل المثال. باستخدام شوكات أو مآخذ العملية للاتصال. يمكنك قراءة المزيد عنها هنا.
عند انتهاء العملية، يتم تشغيل حدث نجاحها، والذي يمكنك ربطه باستخدام الدالة ->then()
. وبالمثل، عندما تفشل عملية ما أو تنتهي مهلتها، ستقوم الحلقة بتحديث حالة تلك العملية والمضي قدمًا. عند الانتهاء من جميع العمليات، سترى حلقة while أنه لم يعد هناك شيء يمكن انتظاره، ثم تتوقف. هذه هي اللحظة التي يمكن أن تستمر فيها عملية والدك في التنفيذ.
لقد كتبنا منشور مدونة يحتوي على مزيد من المعلومات حول حالات الاستخدام لهذه الحزمة، بالإضافة إلى إجراء مقارنات مع مكتبات PHP غير المتزامنة الأخرى مثل ReactPHP وAmp: http://sitcher.io/blog/asynchronous-php.
composer test
الرجاء مراجعة سجل التغيير لمزيد من المعلومات عما تغير مؤخرًا.
يرجى الاطلاع على المساهمة للحصول على التفاصيل.
إذا وجدت خطأً يتعلق بالأمان، فيرجى إرسال بريد إلكتروني إلى العنوان [email protected] بدلاً من استخدام أداة تعقب المشكلات.
أنت حر في استخدام هذه الحزمة، ولكن إذا وصلت إلى بيئة الإنتاج الخاصة بك، فإننا نقدر بشدة إرسالك لنا بطاقة بريدية من مسقط رأسك، مع ذكر الحزمة (الحزم) التي تستخدمها.
عنواننا هو: سباتي، كرويكسترات 22، 2018 أنتويرب، بلجيكا.
ننشر جميع البطاقات البريدية المستلمة على موقع شركتنا.
رخصة معهد ماساتشوستس للتكنولوجيا (MIT). يرجى الاطلاع على ملف الترخيص لمزيد من المعلومات.