13.

PHP sleep/usleep 待機処理完全ガイド

編集
この記事の要点
  • sleep($秒) 秒単位、usleep($マイクロ秒) 1/100万秒単位、time_nanosleep($秒, $ナノ秒) ナノ秒単位
  • リクエスト処理中の sleepPHP-FPM ワーカーをブロックするので原則 NG
  • Laravel での遅延ジョブは $job->delay(now()->addMinutes(5)) をキューに投げる
  • バッチ / cron の代替として一時的に sleep するのは可、ただし 5 秒以上は cron に切り出す
  • HTTP リトライ時は固定 sleep でなく 指数バックオフ + ジッター2^n * 100ms + rand

基本: sleep / usleep / time_nanosleep

<?php
// 秒単位の待機(最も基本)
sleep(2);                  // 2 秒待つ

// マイクロ秒単位(1秒 = 1,000,000 マイクロ秒)
usleep(500000);            // 0.5 秒待つ
usleep(100000);            // 100 ミリ秒

// ナノ秒単位(1秒 = 1,000,000,000 ナノ秒)
time_nanosleep(0, 500000000);  // 0.5 秒待つ
time_nanosleep(2, 0);          // 2 秒待つ(sleep(2) と同等)

// 戻り値:
//   sleep:           成功 0、中断時に残秒数
//   usleep:          void
//   time_nanosleep:  成功 true、中断時に残時間の配列

用途別の使い分け

用途推奨関数備考
1 秒以上の単純な待ちsleep()最もシンプル
ミリ秒単位の精密待機usleep()API のレート制御等
シグナル割り込みを検知したいtime_nanosleep()残時間を返す
絶対時刻まで待つtime_sleep_until($ts)UNIX タイム指定
マイクロ秒精度のタイマー測定hrtime(true) 差分sleep ではなく計測用

Web リクエスト処理中の sleep は危険

PHP-FPM はワーカープロセスでリクエストを処理しているため、sleep(10) を入れるとそのワーカーが 10 秒間ブロックします。同時実行数 50 に対し sleep 入りエンドポイントへ 100 リクエスト来れば、50 件が 504 Gateway Timeout 等で詰まります。

// ❌ アンチパターン: Web 経由の処理で sleep を使う
public function send(Request $request)
{
    Mail::to($request->email)->send(new Welcome());
    sleep(5);                          // ←ワーカーが 5 秒ブロック!
    Mail::to($request->email)->send(new FollowUp());
    return response()->json(['ok' => true]);
}

// ✅ 正解: ジョブキューに非同期で投げ、遅延配信
public function send(Request $request)
{
    Mail::to($request->email)->send(new Welcome());
    SendFollowUpJob::dispatch($request->email)
        ->delay(now()->addMinutes(5));  // 5 分後に実行
    return response()->json(['ok' => true]);
}

Laravel Queue での遅延実行

use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;

class SendReminderJob implements ShouldQueue
{
    use Dispatchable, Queueable;

    public function __construct(public int $userId) {}

    public function handle(): void
    {
        // 実処理
    }
}

// dispatch + delay
SendReminderJob::dispatch($user->id)
    ->delay(now()->addHours(24));

// dispatchAfterResponse: レスポンス送信後に同プロセス内で実行
SendReminderJob::dispatchAfterResponse($user->id);

// onQueue で別キュー
SendReminderJob::dispatch($user->id)
    ->delay(now()->addMinutes(10))
    ->onQueue('reminders');

Laravel Queue は database / redis / sqs 等のバックエンドを使い、php artisan queue:work ワーカーが時刻を見て実行します。Web リクエストはすぐにレスポンスを返せます。

cron との使い分け

処理時間推奨
~100ms(API レート制御)usleep() 直接
1秒~10秒(ポーリング待ち)CLI スクリプトでの sleep()
1 分以上cron / Laravel Scheduler
特定日時に 1 回だけQueue の delay
毎日決まった時刻cron / Scheduler

HTTP リトライでの指数バックオフ

API がエラーを返したときに即リトライすると相手サーバを追い込みます。指数バックオフ + ジッターが定石:

function callWithBackoff(callable $fn, int $maxAttempts = 5)
{
    for ($attempt = 0; $attempt < $maxAttempts; $attempt++) {
        try {
            return $fn();
        } catch (\Throwable $e) {
            if ($attempt === $maxAttempts - 1) throw $e;

            // 2^n * 100ms + 0-100ms のランダムジッター
            $baseMs = (1 << $attempt) * 100;
            $jitter = random_int(0, 100);
            usleep(($baseMs + $jitter) * 1000);
        }
    }
}

// 利用
$response = callWithBackoff(fn() => Http::get('https://api.example.com'));

// Laravel 標準の retry ヘルパも便利
$response = retry(5, function () {
    return Http::throw()->get('https://api.example.com');
}, 100);  // 100ms 起点で指数バックオフ

Laravel HTTP クライアントのリトライ

use Illuminate\Support\Facades\Http;

// 3 回まで、100ms 間隔
$response = Http::retry(3, 100)->get('https://api.example.com');

// バックオフ関数
$response = Http::retry(5, function ($attempt, $exception) {
    return $attempt * 100;  // 100ms, 200ms, 300ms, 400ms, 500ms
})->get('https://api.example.com');

// 特定例外のみリトライ
$response = Http::retry(3, 100, function ($exception, $request) {
    return $exception instanceof ConnectionException;
})->get('https://api.example.com');

シグナル割り込みを処理する

長時間 sleep 中に Ctrl+C 等が来たら、残時間を取って正しく停止できます:

$result = time_nanosleep(10, 0);  // 10 秒待つ
if (is_array($result)) {
    // 中断された場合 ['seconds' => x, 'nanoseconds' => y] が返る
    echo "中断されました。残り {$result['seconds']} 秒\n";
}

// sleep の場合
$remaining = sleep(10);
if ($remaining > 0) {
    echo "中断されました。残り $remaining 秒\n";
}

FAQ

Q: usleep の最大値は?
A: 公式には 1,000,000 マイクロ秒(1 秒)未満が推奨。それ以上は sleep()time_nanosleep() を使ってください。

Q: CLI コマンドでの sleep は問題ない?
A: 問題なし。CLI は 1 プロセスで動くため、Web ワーカーをブロックする問題は起きません。

Q: set_time_limit(0) と sleep を組み合わせて長時間処理したい
A: CLI なら可。Web リクエストで使うと PHP-FPM や Nginx のタイムアウト設定にもひっかかるので、Queue / cron に切り出すべきです。

編集
Post Share
子ページ

子ページはありません

同階層のページ
  1. インストール方法
  2. 文法
  3. Composerのインストール
  4. 内部関数
  5. フレームワーク
  6. エラー一覧
  7. 改行出力
  8. printとechoの違い
  9. シングルクォートとダブルクォートの違い
  10. returnとyieldの違い
  11. var_dumpをログ出力
  12. CSV読み込み
  13. 待機・処理の遅延
  14. ログファイルにエラーを出力する方法
  15. エラーログ出力関数
  16. URLパラメータの配列化
  17. empty, is_null. issetの判定比較表
  18. httpステータスコードの付与
  19. バージョンの確認
  20. php.ini
  21. APIを呼び出す方法
  22. 外部ファイルを呼び出す方法
  23. カンマ区切りの文字列を配列に変換
  24. 配列からランダムに値を取り出す方法
  25. Webスクレイピング

最近更新/作成されたページ