swoole 協程 sleep() 函數導致死鎖的深入分析
本文探討 Swoole 協程中 sleep() 函數可能導致死鎖的問題,并通過代碼示例詳細分析原因及解決方案。
問題重現
在 Swoole 4.8.9 版本中,運行以下代碼會引發“[fatal Error]: all coroutines (count: 1) are asleep – deadlock!”錯誤:
<?php use SwooleProcess; class Deadlock { public function startProcess() { $t = new SwooleProcess(function () { swoole_async_set(['enable_coroutine' => true]); go(function (){ while (true) { SwooleCoroutineSystem::sleep(1); var_dump('dd'); } }); }); $t->start(); } } $proc = new Process(function () { swoole_async_set(['enable_coroutine' => false]); $cls = new Deadlock(); SwooleTimer::after(1000, function () use ($cls) { $cls->startProcess(); // 延遲一秒后啟動子進程 }); }); $proc->start();
根源剖析
代碼中,Deadlock 類啟動一個子進程,并在子進程中啟用協程,該協程無限循環調用 SwooleCoroutineSystem::sleep(1)。關鍵在于父進程的配置和調用時機:
- 父進程禁用協程: swoole_async_set([‘enable_coroutine’ => false]) 在父進程中禁用了協程。
- 延遲調用: 父進程使用 SwooleTimer::after(1000, …) 延遲一秒后,才調用 startProcess() 啟動子進程中的協程。
由于父進程未啟用協程,當子進程中的協程進入 sleep() 狀態后,系統中沒有其他可執行的協程,導致所有協程休眠,從而引發死鎖。
解決方案
為了避免死鎖,需要保持父進程和子進程的協程環境一致性。以下兩種方法可以解決問題:
方法一:父進程啟用協程
在父進程中啟用協程,允許父進程在子進程協程休眠時繼續執行其他任務:
<?php // ... (Deadlock 類代碼不變) ... $proc = new Process(function () { swoole_async_set(['enable_coroutine' => true]); // 啟用父進程協程 $cls = new Deadlock(); SwooleTimer::after(1000, function () use ($cls) { $cls->startProcess(); }); }); $proc->start();
方法二:子進程禁用協程 (不推薦)
在子進程中禁用協程,雖然避免了死鎖,但失去了協程帶來的并發優勢:
<?php use SwooleProcess; class Deadlock { public function startProcess() { $t = new SwooleProcess(function () { // swoole_async_set(['enable_coroutine' => true]); // 禁用子進程協程 for (; ;) { sleep(1); // 使用同步sleep var_dump('dd'); } }); $t->start(); } } // ... (父進程代碼不變) ...
推薦使用方法一,即在父進程中啟用協程,以充分利用 Swoole 協程的優勢。 方法二雖然解決了死鎖,但失去了協程的并發性,降低了程序效率。
通過以上分析和修改,我們可以有效避免 Swoole 協程 sleep() 函數導致的死鎖問題,確保程序的穩定性和高效性。
? 版權聲明
文章版權歸作者所有,未經允許請勿轉載。
THE END