Rubyスレッドの使用方法:わかりやすいチュートリアル
Rubyのスレッドとは何ですか?
スレッドにより、Rubyプログラムは同時に複数のことを実行できます。
次のようなもの :
- 複数のファイルを読み取る
- 複数のウェブリクエストの処理
- 複数のAPI接続を確立する
スレッドを使用した結果、マルチスレッドのRubyプログラムが作成され、作業をより迅速に行うことができます。
しかし、1つの警告…
Rubyアプリケーションを実行するデフォルトの方法であるMRI(MatzのRuby Interpreter)では、 i/oバウンドアプリケーションを実行する場合にのみスレッドの恩恵を受けます。 。
この制限は、 GIL(グローバルインタープリターロック)のために存在します。 。
JRubyやRubiniusなどの代替Rubyインタープリターは、マルチスレッドを最大限に活用します。
では、スレッドとは何ですか?
スレッドはワーカー、つまり実行単位です。
すべてのプロセスには少なくとも1つのスレッドがあり、オンデマンドでさらに作成できます。
コード例を見たいと思います。
ただし、最初に、CPUバウンドアプリケーションとI/Oバウンドアプリケーションの違いについて説明する必要があります。
I/Oバウンドアプリケーション
I/Oバウンドアプリ 外部リソースを待つ必要があるものです:
- APIリクエスト
- データベース(クエリ結果)
- ディスク読み取り
スレッドは、リソースが使用可能になるのを待機している間、停止することを決定できます。これは、別のスレッドが実行されてその処理を実行でき、待機時間を無駄にしないことを意味します。
i/oバウンドアプリの一例 はWebクローラーです。
リクエストごとに、クローラーはサーバーが応答するのを待つ必要があり、待っている間は何もできません。
ただし、スレッドを使用している場合は…
一度に4つのリクエストを作成し、応答が戻ってきたら処理することができます。これにより、ページをより速くフェッチできるようになります。
次に、コード例を示します。
Rubyスレッドの作成
Thread.new
を呼び出すことで、新しいRubyスレッドを作成できます。 。
このスレッドを実行するために必要なコードを含むブロックを必ず渡してください。
Thread.new { puts "hello from thread" }
とても簡単ですよね?
ただし。
次のコードがある場合、スレッドからの出力がないことに気付くでしょう:
t = Thread.new { puts 10**10 } puts "hello"
問題は、Rubyがスレッドの終了を待たないことです。
join
を呼び出す必要があります 上記のコードを修正するためのスレッドのメソッド:
t = Thread.new { puts 10**10 } puts "hello" t.join
複数のスレッドを作成する場合は、それらを配列内に配置して、join
を呼び出すことができます。 すべてのスレッドで。
例 :
threads = [] 10.times { threads << Thread.new { puts 1 } } threads.each(&:join)
Rubyスレッドの探索中に、ドキュメントが役立つ場合があります:
https://ruby-doc.org/core-2.5.0/Thread.html
スレッドと例外
スレッド内で例外が発生した場合、プログラムを停止したり、エラーメッセージを表示したりすることなく、サイレントに終了します。
次に例を示します:
Thread.new { raise 'hell' }
デバッグの目的で、何か悪いことが起こったときにプログラムを停止したい場合があります。これを行うには、Thread
に次のフラグを設定します。 真実へ:
Thread.abort_on_exception = true
スレッドを作成する前に、必ずこのフラグを設定してください🙂
スレッドプール
処理するアイテムが数百あるとします。各アイテムのスレッドを開始すると、システムリソースが破壊されます。
次のようになります:
pages_to_crawl = %w( index about contact ... ) pages_to_crawl.each do |page| Thread.new { puts page } end
これを行うと、サーバーに対して何百もの接続が開始されるため、おそらくそれは良い考えではありません。
1つの解決策は、スレッドプールを使用することです。
スレッドプールを使用すると、いつでもアクティブなスレッドの数を制御できます。
独自のプールを構築することもできますが、お勧めしません。次の例では、セルロイドジェムを使用してこれを行っています。
注:セルロイドは現在メンテナンスされていませんが、ワーカープールの一般的な考え方は引き続き適用されます。
require 'celluloid' class Worker include Celluloid def process_page(url) puts url end end pages_to_crawl = %w( index about contact products ... ) worker_pool = Worker.pool(size: 5) # If you need to collect the return values check out 'futures' pages_to_crawl.each do |page| worker_pool.process_page(page) end
今回は5つのスレッドのみが実行され、スレッドが終了すると次のアイテムが選択されます。
競合状態およびその他の危険
これは非常にクールに聞こえるかもしれませんが、コード全体にスレッドを振りかける前に、並行コードに関連するいくつかの問題があることを知っておく必要があります。
たとえば、スレッドは競合状態になりがちです。
競合状態 物事が順不同で起こり、混乱するときです。
発生する可能性のあるもう1つの問題は、デッドロックです。これは、1つのスレッドが(ミューテックスのようなロックシステムを使用して)あるリソースへの排他的アクセスを保持し、それを解放しないため、他のすべてのスレッドがアクセスできなくなる場合です。
これらの問題を回避するには、生のスレッドを避け、すでに詳細を処理しているいくつかの宝石に固執するのが最善です。
その他のスレッドジェム
スレッドプールにはすでにセルロイドを使用していますが、並行性に焦点を当てた他の多くの宝石をチェックする必要があります:
- https://github.com/grosser/parallel
- https://github.com/chadrem/workers
- https://github.com/ruby-concurrency/concurrent-ruby
わかりました。ルビースレッドについて1つか2つ学んだことを願っています。 !
この記事が役に立った場合は、共有してください。 友達と一緒に学べるように🙂
-
Rubyエイリアスキーワードの使用方法
Rubyメソッドに別の名前を付けるには、次の2つの方法があります。 エイリアス(キーワード) alias_method 彼らはわずかに異なる方法で同じことをするので、これは紛らわしいトピックになる可能性があります。 この画像は違いの要約です : しっかりと理解するために、これらの違いをさらに詳しく調べてみましょう! エイリアスキーワード まず、aliasがあります 、これはRubyキーワードです(ifなど) 、def 、class 、など) このように見えます : alias print_something puts print_something 1 prin
-
RubyでStructとOpenStructを使用する方法
Rubyの構造体とは何ですか? 構造体は組み込みのRubyクラスであり、値オブジェクトを生成する新しいクラスを作成するために使用されます。値オブジェクトは、関連する属性を一緒に格納するために使用されます。 ここに例があります : Point 2つの座標(x &y 。 このデータはさまざまな方法で表すことができます。 いいね : 配列[10, 20] ハッシュ{ x: 10, y: 10 } オブジェクトPoint.new(10, 20) 複数のPointを使用する場合 、オブジェクトアプローチを使用することをお勧めします。 しかし… これら2つの値を一緒に格納するた