0Pricing
PHP Academy · บทเรียน

โมเดลการทำงานพร้อมกันของ 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 ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ

บทเรียนทั้งหมดในหลักสูตรนี้

  1. โมเดลการทำงานพร้อมกันของ PHP
  2. ไฟเบอร์ของ PHP 8.1
  3. ลูปเหตุการณ์ด้วย ReactPHP
  4. เซิร์ฟเวอร์ประสิทธิภาพสูงด้วย Swoole
← กลับไปที่ PHP Academy