Why Sinatra request takes EM thread?
Ok, so it seems Sinatra starts Thin in threaded mode by default causing the above behavior.
You can add
set :threaded, false
in your Sinatra configure section and this will prevent the Reactor defering requests on a separate thread, and blocking when under load.
Source1
Source2
Is Sinatra multi threaded?
tl;dr Sinatra works well with Threads, but you will probably have to use a different web server.
Sinatra itself does not impose any concurrency model, it does not even handle concurrency. This is done by the Rack handler (web server), like Thin, WEBrick or Passenger. Sinatra itself is thread-safe, meaning that if your Rack handler uses multiple threads to server requests, it works just fine. However, since Ruby 1.8 only supports green threads and Ruby 1.9 has a global VM lock, threads are not that widely used for concurrency, since on both versions, Threads will not run truly in parallel. The will, however, on JRuby or the upcoming Rubinius 2.0 (both alternative Ruby implementations).
Most existing Rack handlers that use threads will use a thread pool in order to reuse threads instead of actually creating a thread for each incoming request, since thread creation is not for free, esp. on 1.9 where threads map 1:1 to native threads. Green threads have far less overhead, which is why fibers, which are basically cooperatively scheduled green threads, as used by the above mentioned sinatra-synchrony, became so popular recently. You should be aware that any network communication will have to go through EventMachine, so you cannot use the mysql
gem, for instance, to talk to your database.
Fibers scale well for network intense processing, but fail miserably for heavy computations. You are less likely to run into race conditions, a common pitfall with concurrency, if you use fibers, as they only do a context switch at clearly defined points (with synchony, whenever you wait for IO). There is a third common concurrency model: Processes. You can use preforking server or fire up multiple processes yourself. While this seems a bad idea at first glance, it has some advantages: On the normal Ruby implementation, this is the only way to use all your CPUs simultaniously. And you avoid shared state, so no race conditions by definition. Also, multiprocess apps scale easily over multiple machines. Keep in mind that you can combine multiple process with other concurrency models (evented, cooperative, preemptive).
The choice is mainly made by the server and middleware you use:
- Multi-Process, non-preforking: Mongrel, Thin, WEBrick, Zbatery
- Multi-Process, preforking: Unicorn, Rainbows, Passenger
- Evented (suited for sinatra-synchrony): Thin, Rainbows, Zbatery
- Threaded: Net::HTTP::Server, Threaded Mongrel, Puma, Rainbows, Zbatery, Thin[1], Phusion Passenger Enterprise >= 4
[1] since Sinatra 1.3.0, Thin will be started in threaded mode, if it is started by Sinatra (i.e. with ruby app.rb
, but not with the thin
command, nor with rackup
).
How to stop a background thread in Sinatra once the connection is closed
Eric's answer was close, but what it does is closing the response body (not the client connection, btw) once your twitter stream closes, which normally never happens. This should work:
require 'sinatra/streaming' # gem install sinatra-contrib
# ...
get '/' do
stream(:keep_open) do |out|
# ...
out.callback { http.conn.close_connection }
out.errback { http.conn.close_connection }
end
end
Sinatra with thin, multi thread do not work
Thin can be multi-threaded, but only if you configure it to be so, by default it is single-threaded (evented). From the answer to the question you linked to:
since Sinatra 1.3.0, Thin will be started in threaded mode, if it is started by Sinatra (i.e. with
ruby app.rb
, but not with thethin
command, nor withrackup
).
There doesn’t appear to be a way to get rackup
to pass the threaded option through to Thin, so you will need to use either thin start --threaded
or ruby my_app.rb
to get threading on Thin.
Is Sinatra multi threaded?
tl;dr Sinatra works well with Threads, but you will probably have to use a different web server.
Sinatra itself does not impose any concurrency model, it does not even handle concurrency. This is done by the Rack handler (web server), like Thin, WEBrick or Passenger. Sinatra itself is thread-safe, meaning that if your Rack handler uses multiple threads to server requests, it works just fine. However, since Ruby 1.8 only supports green threads and Ruby 1.9 has a global VM lock, threads are not that widely used for concurrency, since on both versions, Threads will not run truly in parallel. The will, however, on JRuby or the upcoming Rubinius 2.0 (both alternative Ruby implementations).
Most existing Rack handlers that use threads will use a thread pool in order to reuse threads instead of actually creating a thread for each incoming request, since thread creation is not for free, esp. on 1.9 where threads map 1:1 to native threads. Green threads have far less overhead, which is why fibers, which are basically cooperatively scheduled green threads, as used by the above mentioned sinatra-synchrony, became so popular recently. You should be aware that any network communication will have to go through EventMachine, so you cannot use the mysql
gem, for instance, to talk to your database.
Fibers scale well for network intense processing, but fail miserably for heavy computations. You are less likely to run into race conditions, a common pitfall with concurrency, if you use fibers, as they only do a context switch at clearly defined points (with synchony, whenever you wait for IO). There is a third common concurrency model: Processes. You can use preforking server or fire up multiple processes yourself. While this seems a bad idea at first glance, it has some advantages: On the normal Ruby implementation, this is the only way to use all your CPUs simultaniously. And you avoid shared state, so no race conditions by definition. Also, multiprocess apps scale easily over multiple machines. Keep in mind that you can combine multiple process with other concurrency models (evented, cooperative, preemptive).
The choice is mainly made by the server and middleware you use:
- Multi-Process, non-preforking: Mongrel, Thin, WEBrick, Zbatery
- Multi-Process, preforking: Unicorn, Rainbows, Passenger
- Evented (suited for sinatra-synchrony): Thin, Rainbows, Zbatery
- Threaded: Net::HTTP::Server, Threaded Mongrel, Puma, Rainbows, Zbatery, Thin[1], Phusion Passenger Enterprise >= 4
[1] since Sinatra 1.3.0, Thin will be started in threaded mode, if it is started by Sinatra (i.e. with ruby app.rb
, but not with the thin
command, nor with rackup
).
Understand ruby multi-threading in simple Sinatra app
There is a Global Interpreter Lock in MRI that prevents two threads running together. In your example, your long thread is sleeping (doing nothing) thus MRI can suspend it and run the other thread. IF both threads where taking 100% of cpu time, then you would expect one of the threads to wait for another. If you had JRuby, then you would have each thread taking 100% of CPU time from each core (assuming you have multicore processor), thus your threads wouldnt slow down.
Following article should answer your question in depth: http://ablogaboutcode.com/2012/02/06/the-ruby-global-interpreter-lock/
sinatra service
You might want to look at async-sinatra.
Does the main thread 'always' run in a ruby web server, like Sinatra?
The following code works fine for me - tested on OS X local machine I was able to get 1500+ real threads running with thin and ruby 1.9.2. On Heroku cedar stack, I can get about 230 threads running before I get an error when creating a thread.
In both cases all the threads seem to finish when they are supposed to - 2 minutes after launching them. '/' is rendered in about 60 ms on Heroku, and then the 20 threads run for 2 minutes each.
If you refresh / a few times, then wait a few minutes, you can see the threads finishing. The reason I tested for 2 minutes was that heroku has a 30 second limit on responses, cutting you off if you take more than that amount of time. But this does not seem to effect background threads.
$threadsLaunched = 0
$$threadsDone = 0
get '/' do
puts "#{Thread.list.size} threads"
for i in 1..20 do
$threadsLaunched = $threadsLaunched + 1
puts "Creating thread #{i}"
Thread.new(i) do |j|
sleep 120
puts "Thread #{j} done"
$threadsDone = $threadsDone + 1
end
end
puts "#{Thread.list.size} threads"
erb :home
end
(home.erb)
<div id="content">
<h1> Threads launched <%= $threadsLaunched.to_s %> </h1>
<h1> Threads running <%= Thread.list.count.to_s %> </h1>
<h1> Threads done <%= $threadsDone.to_s %> </h1>
</div> <!-- id="content" -->
Related Topics
Does Ruby Provide a Constant_Added Hook Method
How to Make an Infowindow Automatically Display as Open with Google-Maps-For-Rails
Understanding Ruby Method Parameters Syntax
Ruby: Sorting 2 Arrays Using Values from One of Them
Pow Not Loading Gem Properly While Rails S Works
When Joining Table, Rails Anyway Makes Additional Request When Accessing Fields from Joined Table
Make Map Marker Direct Link Onclick for Gmaps4Rails
Rails 3 - Devise/Actionmailer/Ruby-Smtp Causing a Segmentation Fault
How to Get Data from Database in Ruby
Rails Get Index of "Each" Loop
Utc Time Resets to 2000-01-01 (Ruby). How to Prevent the Time from Resetting
Definition of Method in Top Level
Assets Precompiling Error with Jquery UI Plugin
Sinatra Doesn't Know This Ditty Even When Default Route Is Implemented with Modular Style
Prawn Gem: How to Create the .Pdf from an *Existing* File (.Xls)
Delete from Database with Specific Details in Ruby
Receving the Undefined Method 'Generators' Error
How to Make the Days of the Month Be Printed According to Each Day (Ex: Su Mo Tu We...Etc)