【Ruby】sleepとは?使い方や実務での利用方法を初心者向けにわかりやすく解説

はじめに

Rubyでプログラムを書いていると、一時的に処理を止めたい場面が出てくることがあります。
たとえば複数の処理を順番に実行したい場合や、一定のインターバルをあけてAPIを呼び出したいような場合です。
こうしたケースでよく使われるのがsleepメソッドです。
sleepを使うと、指定した秒数だけプログラムの実行を停止できます。

ただ、むやみにsleepを使うと処理全体が止まり、思わぬトラブルを招くケースもあるでしょう。
そこで、本記事ではRubyのsleepメソッドの正しい使い方や、実務での活用シーンをできるだけ具体的に紹介していきます。

この記事を読むとわかること

  • Rubyのsleepメソッドの基本的な使い方
  • 実務で考えられる具体的な利用シーン
  • メリットと注意点、そして他のスレッドとの組み合わせ方
  • 定期実行やタイミング調整におけるコード例
  • sleepを使う際のパフォーマンス面の工夫

Rubyにおけるsleepの概要

Rubyのsleepメソッドは、プログラムの動作を指定した秒数だけ停止させるためのメソッドです。
たとえば「3秒だけ待機したい」という場合は、sleep(3) のように記述します。
単純に使えるメソッドですが、実際の開発現場でもたびたび登場します。

その背景には、次のような状況があるのではないでしょうか。

  • リクエストを何度も連続して送ると、相手先サーバーに負荷がかかってしまう
  • 処理間に待ち時間をはさんでユーザーの入力を待つ
  • バッチ処理でリトライ機能を実装する際に、再試行までの間隔を設けたい

こういったシーンでsleepが活躍すると、適切に時間を空けながら処理を進めることができます。
一方、何も考えずにsleepを使うと、プログラム全体が完全に停止してしまう点には注意が必要です。

sleepの基本的な使い方

sleepメソッドのシンプルな書き方

sleepの使い方はとてもシンプルです。
以下のように、ただsleep(秒数)と記述するだけで、その秒数分処理が止まります。

puts "処理開始"

sleep(2)  # 2秒間停止

puts "処理再開"

この場合、puts "処理開始" という行が出力されてから2秒間待機し、その後に puts "処理再開" が実行されます。

sleepの引数は整数だけでなく浮動小数点数も指定できます。
たとえば sleep(0.5) のように書くと、0.5秒だけ待機します。

実行結果がどのようになるか

上記のサンプルコードを実行すると、コンソールに「処理開始」という文字列が出たのち、2秒後に「処理再開」という文字列が表示されます。
このように、sleepが呼ばれてから指定した秒数が過ぎるまで、プログラムは何もしません。
Rubyでは、sleepの戻り値は、実際に停止した秒数が返ってきます。

とはいえ、多くの場合は戻り値を活用せず、「処理を止める」という目的で使われることがほとんどです。
単純なメソッドですが、使いどころを誤るとかえって問題につながるケースもあるため、よく目的を考えてから利用しましょう。

実務での利用シーン

待機処理やAPI呼び出しの間隔調整

実務では、外部APIの呼び出し頻度を制御したいシーンがよくあります。
一度に大量のリクエストを送ると、相手先のサーバーやネットワークに負荷がかかります。
さらには、一定時間に送れるリクエスト数が制限されているAPIも珍しくありません。

そういったケースでは、sleepを挟んでリクエストの送信間隔を調整できます。
例えば、1秒ごとにAPIを呼び出すときには、以下のようなコードが考えられます。

10.times do |i|
  puts "APIを呼び出します (#{i + 1}回目)"
  # ここで実際のAPI呼び出しを行うと想定
  sleep(1)
end

これにより、何度も連続でリクエストを送ることによる過度な負荷を軽減できるでしょう。

ユーザー入力のタイミングを待つ

ユーザーに何らかの入力を求め、入力が終わるまで時間を空けたい場合もあります。
インタラクティブなスクリプトを作る場合、ユーザーが入力している間に少し待機をしたいときがあるかもしれません。

ただし、通常はユーザーの入力待ちには gets のようなメソッドを使うことが多いです。
明示的にsleepを使って待機するより、ユーザーが入力を終えるタイミングで処理を進める仕組みを作ったほうが自然なケースも多いでしょう。
それでも、あえて短時間だけ待ち時間を設定したいというなら、sleepが役立つ場面はあります。

バッチ処理やリトライの間隔

バッチ処理で外部システムと連携するときにエラーが起きた場合、リトライの仕組みを入れることがあります。
このとき、一定の時間をおいて再試行したいなら、sleepで待機させるのがシンプルです。

例えば、HTTPリクエストがタイムアウトになった場合に再試行する際、1秒ずつ待って最大3回試すコード例をあげると、次のようになるでしょう。

max_retries = 3
current_retry = 0

begin
  # HTTPリクエストや外部サービスとの通信
  puts "通信を試みます"
  # 成功したと仮定

rescue => e
  current_retry += 1
  if current_retry <= max_retries
    puts "エラーが発生しました。#{current_retry}回目のリトライを行います"
    sleep(1)
    retry
  else
    puts "最大リトライ回数に達したので処理を終了します"
  end
end

このサンプルのように、短時間だけ待機して再試行するというロジックは実務でよく使われます。

sleepを使うメリットと注意点

メリット

sleepを活用する最大のメリットは、処理を簡単に止められる点です。
複雑なコードを書かなくても、1行だけで時間を調整できるので、プログラム全体の見通しを保ちやすくなります。

また、外部リソースへのアクセスが頻繁になるのを防ぐことができるので、相手先の負荷を下げたり、レートリミットを回避したりするときにも役立つでしょう。
シンプルであるがゆえに、やりたいことが明確ならば非常に使いやすい手段といえます。

注意点

一方で、sleepはプロセス全体を停止させます。
シングルスレッドで動くスクリプトなら、その間は何も処理が進まなくなります。
複数の処理を並行で行う必要がある場面では、必要以上にsleepを使いすぎると応答が遅くなる危険があります。

また、ユーザーがリアルタイムで操作するシステムでsleepを多用すると、操作が「もっさり」してしまうかもしれません。
そのため、必要以上に待ち時間を長くしない工夫や、本当にsleepが必要か見極めることが大切です。

sleepの使いどころを誤ると、ユーザー体験が悪くなる可能性があります。
処理を並行化したいときは、スレッドや非同期処理の仕組みなどを検討するのがよいでしょう。

複数スレッドでsleepを使う方法

Rubyでは、スレッドを作成して複数の処理を並行して実行することが可能です。
このとき、スレッドごとにsleepを呼び出すこともできます。
例えば、以下のようにスレッドを2つ走らせて、それぞれでsleepを実行してみるコードを考えてみましょう。

thread_a = Thread.new do
  puts "スレッドA: 開始"
  sleep(3)
  puts "スレッドA: 終了"
end

thread_b = Thread.new do
  puts "スレッドB: 開始"
  sleep(1)
  puts "スレッドB: 終了"
end

thread_a.join
thread_b.join
puts "メイン処理も完了"

この例では、スレッドAとスレッドBがそれぞれ独立して動きます。
スレッドAは3秒待機し、スレッドBは1秒待機します。
それぞれのスレッドがsleepしている間、他のスレッドは処理を継続できるため、並行処理が成立します。

タイミングの調整例

複数のスレッドを扱うとき、特定のタイミングで待機を入れたい場合もあるでしょう。
たとえば、あるスレッドはすぐに処理を開始してほしいが、別のスレッドは少し遅らせてスタートさせたい場合などです。
そうしたときに、スタート前に sleep(5) のように挟むだけで、開始タイミングを手軽に調整できます。

ただし、スレッドが増えすぎるとコードが複雑になり、デバッグが難しくなることもあります。
sleepを使ってタイミングを合わせる方法はわかりやすい一方で、設計次第では混乱が生じることがあるため、全体の見通しを考慮しながら実装することをおすすめします。

特定時間ごとに処理を実行する方法

アプリケーションを作っていると、一定時間ごとに繰り返し処理を行いたいケースが出てくるかもしれません。
たとえば、10分ごとにファイルをバックアップするとか、1分おきにログを出力するなどの例が挙げられます。

sleepを使ったもっともシンプルな書き方は、次のような形式です。

loop do
  # ここでやりたい処理を実行
  puts "定期処理を実行しました"

  # このあとのインターバルを設定
  sleep(60)  # 60秒待機
end

このコードは、無限ループの中で処理を実行し、終わったら60秒間スリープしてから再度ループに入ります。
結果として、1分に1回、指定した処理を繰り返す仕組みになります。

ただし、実際にはバッチ処理をcronなどのスケジューリングシステムで動かすほうが管理しやすいことが多いです。
短めの間隔でループを回すスクリプトを常駐させると、メモリ消費や管理コストが増える可能性があるからです。
それでも、簡易的なツールや開発中のテスト目的などでsleepを使う方法は便利でしょう。

定期実行のサンプルコード

もう少し具体的な例として、一定時間ごとにデータを取得して表示するサンプルを書いてみます。

require 'net/http'
require 'json'

def fetch_data
  uri = URI.parse("https://example.com/api/data")
  response = Net::HTTP.get_response(uri)
  JSON.parse(response.body)
end

loop do
  data = fetch_data
  puts "取得したデータ: #{data}"

  sleep(10)  # 10秒ごとにデータを取得
end

このコードは、fetch_data というメソッドでAPIからデータを取得し、取得結果を表示してから10秒待機するループを延々と繰り返します。
ただし、本当に長時間連続で動かすなら、例外処理やエラー時のリトライ、メモリリークがないかの確認など、実務では考慮すべき点が多数あります。

フレームワークやライブラリとの組み合わせ

Railsにおけるsleepの使用例

RailsなどのWebフレームワーク上でも、Rubyの基本的なメソッドとしてsleepを使うことができます。
ただし、Railsにおいてはリクエストを受け付けるたびにコントローラが呼び出されるため、その中でsleepを使うと処理が停止している間はレスポンスが返せない状態になります。

以下は単純なイメージの例です。

class ExampleController < ApplicationController
  def index
    # なにか処理があって...
    sleep(2)
    render plain: "2秒待ってからレスポンスを返すサンプル"
  end
end

このコードでは、コントローラの index アクションで2秒間の待機を行い、その後にテキストを返しています。
実務でこのようにsleepを入れるケースはあまり多くありませんが、遅延応答のテストや実験的な目的なら考えられます。

本番環境で同様のアプローチを多用すると、アプリケーションサーバーのスレッドやプロセスが占有され、リクエストが詰まる可能性が高いです。
どうしても遅延を入れたい状況なら、ジョブキューやバックグラウンド処理を利用する方法などを検討するほうがよいでしょう。

テストコードでsleepを利用するパターン

Rubyでテストコードを書くときに、処理の結果を少し待ちたい場面があります。
非同期処理やバックグラウンドジョブが実行されるまで、テストが先に進んでしまうと本来の挙動を確認できないからです。

たとえば、RSpecでエンドポイントのテストを書いていて、バックグラウンドジョブが完了するのを待ちたいときに短時間だけsleepを入れることがあります。

it "バックグラウンドジョブが完了してから結果が変わっていることを確認する" do
  post "/start_job"
  sleep(1)  # ジョブ完了までちょっと待機

  # DBに反映されているか確認
  result = SomeModel.last
  expect(result.status).to eq("done")
end

あまり推奨されない方法ではありますが、どうしても簡易的に時間をかけて待つしか手段がない場合には有効です。
より適切な方法としては、ジョブのステータスをポーリングしたり、テストフレームワークの機能でジョブの完了を検知したりするアプローチが考えられます。
しかし状況によっては、sleepでの待機が手早く済むというメリットもありますので、必要最低限の使用にとどめるのが望ましいです。

パフォーマンスを意識したsleepの使い方

sleepはシンプルで便利ですが、不必要に長い待ち時間を設定するとパフォーマンスを落とす要因になりかねません。
特にWebアプリケーションのリクエスト処理などは、ひとたびsleepを入れると、そのリクエスト処理が完了するまでサーバーが応答できずに待機してしまいます。

複数のユーザーが同時アクセスする環境で、メインスレッドで長いsleepを入れると応答が遅くなる恐れがあります。
パフォーマンス要件が厳しいプロジェクトでは、sleepの使用を慎重に検討しましょう。

無駄な待機を減らす工夫

もし「10秒待てば確実に完了するだろう」という前提だけでsleep(10)を行っているなら、さらに短い値にできるかを考えるとよいでしょう。
あるいは、繰り返し実行される処理のループでsleepを使っている場合は、より効率的なタイミング管理ができないか見直すことも重要です。

また、sleepの代わりにイベントやコールバックを利用して「処理が完了したら次へ進む」仕組みにすると、待ち時間を最小限に抑えられます。
例えば、ファイルの読み込みが終わった時点で呼ばれるコールバックや、外部APIがレスポンスを返したタイミングで処理を進めるアプローチを取り入れると、余計な待ち時間は発生しません。

まとめ

ここまで、Rubyのsleepメソッドに関するさまざまな情報をお伝えしました。
処理を一時的に停止する方法として、sleepはとてもシンプルで使いやすい反面、プロセス全体が止まってしまうことによる弊害もあります。

バッチ処理やAPIコールの間隔調整など、実務でも使い道は多いですが、アプリケーションのパフォーマンスやユーザーへの影響を常に考慮する必要があるでしょう。
スレッドを活用することで、並行処理を行いながら一部だけを待機させることも可能ですし、定期実行の仕組みを作る際にもsleepが役立ちます。

ただし、RailsなどのWebフレームワーク上での利用には注意が必要です。
リクエストの最中にsleepを入れると、想定外に処理が遅れることがあるからです。
テストコードでsleepを使う場合も、本質的には完了を待機する手段としてsleep以外の方法が用意されているなら、そちらを検討するのが望ましいでしょう。

最終的には、目的に合わせて合理的にsleepを選択・配置することが大切です。
実行する環境や要件を考慮しながら、どこで何秒待機させるか、あるいは別の方法を使うべきかを慎重に検討してみてください。
こうした視点を持つことで、sleepが必要な場面では適切に使い、不必要な待機によるパフォーマンス低下を避けることができるでしょう。

Rubyをマスターしよう

この記事で学んだRubyの知識をさらに伸ばしませんか?
Udemyには、現場ですぐ使えるスキルを身につけられる実践的な講座が揃っています。