โมเดลการทำงานพร้อมกันของ PHP
ทำความเข้าใจ I/O แบบบล็อกและจุดที่อะซิงโครนัสช่วยได้
โมเดลการทำงานพร้อมกันของ PHP เป็นบทเรียน PHP Academy ฟรีบน CoddyKit นี่คือบทเรียนที่ 1 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน PHP Academy และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส PHP Academy มีบทเรียนทั้งหมด 4 บทเรียน
PHP ทำงานแบบซิงโครนัสโดยค่าเริ่มต้น
PHP แบบดั้งเดิมประมวลผลหนึ่งคำขอต่อหนึ่งโพรเซส จากบนลงล่าง โดยมี การรับส่งข้อมูลแบบบล็อก การเรียก file_get_contents() ไปยังเอพีไอที่ช้าจะหยุดสคริปต์ทั้งหมดจนกว่าไบต์จะมาถึง แบบจำลองที่ไม่ใช้สถานะร่วม (สร้างโพรเซสใหม่สำหรับแต่ละคำขอและล้างสถานะเมื่อจบงาน) เรียบง่ายอย่างมาก — แต่ก็หมายความว่าผู้ปฏิบัติงานหนึ่งตัวทำได้เพียงงานเดียวในแต่ละครั้ง บทเรียนนี้จะชี้ให้เห็นว่าจุดใดก่อปัญหาและจุดใดการทำงานอะซิงก์ช่วยได้
วัดผลการรับส่งข้อมูลแบบบล็อก
สังเกตว่าเวลาจริงที่ผ่านไปถูกครอบงำด้วยการรอ ไม่ใช่การคำนวณ คำขอที่ทำต่อกันสามรายการและใช้เวลารายการละหนึ่งวินาทีจะใช้เวลาประมาณสามวินาที แม้ว่า CPU จะว่างตลอดช่วงเวลานั้น
<?php
$start = microtime(true);
foreach (['a', 'b', 'c'] as $job) {
usleep(1_000_000); // simulate a 1s blocking network call
echo "done $job\n";
}
printf("elapsed: %.2fs\n", microtime(true) - $start); // ~3.00sงานผูกกับ CPU เทียบกับงานผูกกับการรับส่งข้อมูล
การทำงานพร้อมกันช่วยเฉพาะงานที่ ผูกกับการรับส่งข้อมูล — เวลาที่ใช้รอเครือข่าย ดิสก์ หรือฐานข้อมูล สำหรับงานที่ ผูกกับ CPU (การทำแฮช การปรับขนาดรูปภาพ การแยกวิเคราะห์) เธรดเดียวก็ทำงานเต็มกำลังอยู่แล้ว การทำงานอะซิงก์เพียงเพิ่มต้นทุนการจัดตาราง ควรวินิจฉัยก่อน:
- การเรียกเอชทีทีพีหรือการสืบค้นฐานข้อมูลจำนวนมากที่ช้า → การทำงานอะซิงก์ช่วยได้มาก
- ลูปตัวเลขที่ทำงานถี่ → ใช้การประมวลผลขนาน (โพรเซส/เธรด) ไม่ใช่วงจรเหตุการณ์
PHP รองรับการขยายระบบมาอย่างไร
ในอดีต PHP ขยายระบบด้วย การทำงานพร้อมกันระดับโพรเซส: PHP-FPM รักษากลุ่มโพรเซสผู้ปฏิบัติงานไว้ แต่ละตัวจัดการคำขอครั้งละหนึ่งรายการ และ OS ทำหน้าที่สลับการทำงาน นี่คือการทำงานพร้อมกันจริงระหว่างคำขอ แต่ไม่ช่วยอะไร ภายในคำขอหนึ่งรายการ — คุณยังไม่สามารถกระจายการเรียกเอพีไอ 50 รายการให้ทำงานพร้อมกันภายในตัวจัดการเดียวด้วยโค้ดแบบบล็อกทั่วไปได้
แนวคิดลูปเหตุการณ์
PHP แบบอะซิงโครนัสนำเสนอลูปเหตุการณ์: เธรดเดียวที่ลงทะเบียนการดำเนินการอินพุต/เอาต์พุตให้ไม่บล็อก จากนั้นเข้าสู่โหมดพักใน stream_select()/epoll จนกว่าการดำเนินการใด ๆ จะพร้อม แล้วเรียกใช้ฟังก์ชันเรียกกลับเมื่อข้อมูลมาถึง เธรดเดียวสามารถจัดการซ็อกเก็ตนับพันได้ เพราะไม่เคยว่างรอซ็อกเก็ตใดซ็อกเก็ตหนึ่งเพียงอย่างเดียว
<?php
// Conceptual loop: poll many non-blocking streams at once
$streams = openManyNonBlockingSockets();
while ($streams) {
$read = $streams; $write = $except = [];
stream_select($read, $write, $except, null); // sleeps until ANY is ready
foreach ($read as $s) {
handleReadyStream($s); // runs only the sockets that have data now
}
}แบบร่วมมือ ไม่ใช่แบบแย่งชิง
PHP แบบอะซิงโครนัสทำงานแบบร่วมมือ: งานจะทำงานต่อไปจนกว่าจะยอมส่งต่อการควบคุมด้วยตัวเองที่จุดอินพุต/เอาต์พุต ไม่มีการแย่งชิงเวลา ดังนั้นการเรียกใช้แบบบล็อกที่ใช้เวลานานหรือลูปที่ใช้ CPU อย่างต่อเนื่องจะทำให้ลูปทั้งหมดหยุดชะงัก — งานอื่นทุกงานจะไม่ได้รับโอกาสทำงาน กฎทองคือ ห้ามเรียกใช้ฟังก์ชันแบบบล็อก (sleep, PDO แบบบล็อก, file_get_contents) ภายในงานของลูปเหตุการณ์ ให้ใช้ฟังก์ชันแบบอะซิงโครนัสที่เทียบเท่ากันของลูป
สามแนวทางใน PHP
PHP สมัยใหม่มีชุดเครื่องมือแบบเป็นชั้นดังนี้:
- ไฟเบอร์ (PHP 8.1, แกนหลัก) — องค์ประกอบพื้นฐานระดับต่ำสำหรับหยุดชั่วคราวและทำงานต่อจากสแตกการเรียก เป็นโครงสร้างพื้นฐาน ไม่ใช่เฟรมเวิร์กเต็มรูปแบบ
- ReactPHP / แอมป์ — ลูปเหตุการณ์ระดับผู้ใช้ที่สร้างบนสตรีม (และปัจจุบันใช้ไฟเบอร์ด้วย) สำหรับอินพุต/เอาต์พุตแบบไม่บล็อกโดยไม่ต้องใช้ส่วนขยาย
- ซวูล / OpenSwoole — ส่วนขยายภาษา C ที่มีโครูทีน เซิร์ฟเวอร์ประสิทธิภาพสูง และการเชื่อมฟังก์ชันแบบบล็อกเข้ากับระบบ
ให้ตรวจสอบสิ่งที่พร้อมใช้งานขณะทำงานก่อนเลือกแนวทาง
<?php
echo 'Fibers: ' . (class_exists('Fiber') ? 'yes' : 'no'), PHP_EOL;
echo 'Swoole ext: ' . (extension_loaded('swoole') ? 'yes' : 'no'), PHP_EOL;
echo 'parallel ext: ' . (extension_loaded('parallel') ? 'yes' : 'no'), PHP_EOL;
echo 'PHP ' . PHP_VERSION, PHP_EOL;พรอมิสจำลองค่าในอนาคต
ก่อนมีไฟเบอร์ PHP แบบอะซิงโครนัสจะแสดงผลลัพธ์ที่ยังรออยู่เป็นพรอมิส: ตัวยึดแทนค่าสำหรับค่าที่จะได้ในภายหลัง คุณผูกฟังก์ชันเรียกกลับผ่าน then() ได้ วิธีนี้ใช้งานได้ แต่ทำให้เกิดสายการเรียกซ้อนกัน ส่วนไฟเบอร์ช่วยให้ไลบรารีซ่อนความซับซ้อนนี้ไว้เบื้องหลัง await ที่ดูเหมือนทำงานแบบซิงโครนัส
<?php
use React\Promise\Promise;
$promise = new Promise(function ($resolve) {
// resolved later when I/O completes
$resolve(42);
});
$promise->then(function ($value) {
echo "got $value\n";
});การทำงานพร้อมกันเทียบกับการประมวลผลแบบขนาน
ควรใช้คำให้แม่นยำ:
- การทำงานพร้อมกัน — งานจำนวนมากกำลังดำเนินการอยู่ โดยสลับกันทำงานบนเธรดเดียว (ลูปเหตุการณ์) เหมาะอย่างยิ่งกับอินพุต/เอาต์พุต
- การประมวลผลแบบขนาน — งานจำนวนมากทำงานพร้อมกันในเวลาเดียวกันบนหลายแกนประมวลผล (กระบวนการ ส่วนขยาย
parallelและตัวทำงานของซวูล) จำเป็นสำหรับงานที่ใช้ CPU
ลูปเหตุการณ์ให้การทำงานพร้อมกัน ไม่ใช่การประมวลผลแบบขนาน ข้อมูลโค้ดด้านล่างแสดงการประมวลผลแบบขนานระดับกระบวนการด้วย proc_open — ตัวทำงานสามตัวทำงานพร้อมกันในกระบวนการของ OS ที่แยกจากกัน
<?php
$procs = [];
foreach (range(1, 3) as $i) {
// Each worker is a separate OS process -> real parallelism
$procs[] = proc_open(
"php -r 'usleep(500000); echo \"worker $i done\\n\";'",
[1 => ['pipe', 'w']], $pipes
);
}
foreach ($procs as $p) { proc_close($p); }
echo "all workers launched in parallel\n";เมื่อไม่ควรเลือกใช้ NOT แบบอะซิงโครนัส
การทำงานแบบอะซิงโครนัสมีต้นทุน: ตัวทำงานที่ทำงานต่อเนื่องยาวนานหมายความว่า PHP จะไม่ล้างสถานะระหว่างคำขออีกต่อไป ดังนั้นหน่วยความจำรั่ว แคชแบบคงที่ และการเชื่อมต่อที่หมดสภาพจึงกลายเป็นความรับผิดชอบของคุณ หากตัวจัดการคำขอเรียกใช้การค้นหาฐานข้อมูลเพียงครั้งเดียวแล้วส่งผลลัพธ์กลับ PHP-FPM จะเรียบง่ายกว่าและเร็วพอ ๆ กัน ควรเลือกใช้การทำงานแบบอะซิงโครนัสเมื่อมีความต้องการกระจายงานอินพุต/เอาต์พุต WebSockets/การสำรวจแบบยาว การสตรีม หรือเซิร์ฟเวอร์ที่ทำงานต่อเนื่อง
การประมวลผลแบบขนานจริง: ส่วนขยายการประมวลผลแบบขนานและกระบวนการ
สำหรับการกระจายงานที่ใช้ CPU อย่างหนัก PHP สามารถทำการประมวลผลแบบขนานในระดับ OS ได้จริง ส่วนขยาย parallel จะสร้างเธรดตัวทำงานที่มีหน่วยความจำแยกอิสระ หรือใช้ proc_open/pcntl_fork เพื่อสร้างกระบวนการ ซึ่งแตกต่างจากลูปเหตุการณ์ตรงที่ใช้หลายแกนประมวลผลพร้อมกัน — เป็นเครื่องมือที่เหมาะสมเมื่อคอขวดอยู่ที่การคำนวณ ไม่ใช่การรอ
<?php
// Requires ext-parallel — runs tasks on separate threads/cores
use parallel\Runtime;
$runtime = new Runtime();
$future = $runtime->run(function (): int {
$sum = 0;
for ($i = 0; $i < 1_000_000; $i++) $sum += $i; // CPU work on another core
return $sum;
});
echo $future->value(), PHP_EOL;ตรวจสอบความเข้าใจ
จะเกิดอะไรขึ้นหากเรียกใช้ sleep(2) แบบบล็อกภายในงานของลูปเหตุการณ์
สรุปทบทวน
ขณะนี้คุณมีแบบจำลองทางความคิดเกี่ยวกับการทำงานพร้อมกันใน PHP แล้ว:
- PHP โดยค่าเริ่มต้นทำงานแบบซิงโครนัส แบบบล็อก และไม่ใช้ทรัพยากรร่วมกัน; FPM ขยายการรองรับข้ามคำขอ ไม่ใช่ภายในคำขอเดียว
- การทำงานแบบอะซิงโครนัสช่วยงานที่ผูกกับอินพุต/เอาต์พุตผ่านลูปเหตุการณ์แบบเธรดเดียวที่ใช้สตรีมแบบไม่บล็อก
- รูปแบบนี้เป็นแบบร่วมมือ — ห้ามบล็อกลูป งานที่ใช้ CPU เป็นเวลานานต้องใช้การประมวลผลแบบขนาน ไม่ใช่การทำงานพร้อมกัน
- ชุดเครื่องมือ PHP: ไฟเบอร์ (องค์ประกอบพื้นฐาน), ReactPHP/แอมป์ (ลูประดับผู้ใช้และพรอมิส), ซวูล (ส่วนขยายโครูทีน)
คำถามที่พบบ่อย
บทเรียน “โมเดลการทำงานพร้อมกันของ PHP” ฟรีหรือไม่
ใช่ — ข้อความเต็มของ “โมเดลการทำงานพร้อมกันของ PHP” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส PHP Academy ให้อัปเกรดเป็น CoddyKit PRO คอร์ส PHP Academy มีบทเรียนทั้งหมด 4 บทเรียน
คุณจะเรียนรู้อะไรในบทเรียน “โมเดลการทำงานพร้อมกันของ PHP”
ทำความเข้าใจ I/O แบบบล็อกและจุดที่อะซิงโครนัสช่วยได้ คุณปฏิบัติ PHP Academy ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน
คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน PHP Academy หรือไม่
ไม่จำเป็นต้องมีประสบการณ์มาก่อน PHP Academy บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 1 จากทั้งหมด 4 บทเรียน
บทเรียน “โมเดลการทำงานพร้อมกันของ PHP” ใช้เวลานานแค่ไหน
บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย
ฉันเขียนและรันโค้ดในบทเรียน PHP Academy นี้ได้ไหม
ได้ บทเรียน PHP Academy ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ
บทเรียนทั้งหมดในหลักสูตรนี้
- โมเดลการทำงานพร้อมกันของ PHP
- ไฟเบอร์ของ PHP 8.1
- ลูปเหตุการณ์ด้วย ReactPHP
- เซิร์ฟเวอร์ประสิทธิภาพสูงด้วย Swoole