How to run a background thread in ruby?
Read the documentation for Thread.new
(which is the same as Thread.start
here)
Thread.start(commands)
runs the commands
method and passes its return value to a thread (which then does nothing). It's blocking because you aren't starting any threads when gets
is called. You want
Thread.start { commands }
Here's a similar demo script that works just like you would expect
def commands
while gets.strip !~ /^exit$/i
puts "Invalid command"
end
abort "Exiting the program"
end
Thread.start { commands }
loop do
puts "Type exit:"
sleep 2
end
Multithreading vs Background jobs in Rails
Sounds like you need a thread pool for performing the operation, and a database thread to commit the results.
You can build one of these really simply:
require 'thread'
db_queue = Queue.new
Thread.new do
while (item = db_queue.pop)
# ... Deal with item in queue
end
end
# Example of supplying a job
db_queue.push(api_response)
# When finished
db_queue.push(nil)
Due to the Global Interpreter Lock in the standard Ruby runtime threads are only really useful for managing many lightly loaded threads. If you need something more heavy-duty, JRuby might be what you're looking for.
Rails best practice: background process/thread?
Threading for background processes in ruby would be a terrible mistake, especially since you're using a multi-process server. Using unicorn with say 4 worker processes would mean that you'd be polling from each of them, which is not what you want. Ruby doesn't really have real threads, it has green threads in 1.8 and a global interpreter lock in 1.9 IIRC. Many gems and libraries are also obnoxiously unthreadsafe.
Using memcache is still your best option and, if you have it set up correctly, you should only see it adding a millisecond or two to the request time. Another option which would give you the benefit of persisting these alerts while incurring minimal additional overhead would be to store these alerts in redis. This would better protect you against things like memcache crashing or server reboots.
For the background jobs you should use a similar approach to what you have now, but there are several off the shelf handlers for this like resque, delayed_job, and a few others. If you absolutely have to use SQS as the backend queue, you might be able to find some code to help you, but otherwise you could write it yourself. This still requires the other daemon to be rebooted whenever there is a code change. In practice this isn't a huge concern as best practices dictate using a deployment system like capistrano where a rule can easily be added to bounce the daemon on deploy. I use monit to watch the daemon process, so restarting it is as easy as telling monit to restart it.
In general, Ruby is not like Java/Objective-C when it comes to threads. It follows the more Unix-like model of process based isolation, but the community has come up with best practices and ways to make this less painful than in other languages. Ruby does require a bit more attention to setting up its stack as it is not as simple as enabling mod_php
and copying some files around, but once the choices and architecture is understood, it is easier to reason about how your application works. The process model, in my opinion, is much better for web apps as it isolates code and state from the effects of other running operations. The isolation also makes the app easier to work with in a distributed system.
Ruby threading/forking with API (Sinatra)
I believe that better way to do it - is to use background jobs. While your worker executes some long-running tasks, it is unavailable for new requests. With background jobs - they do the work, while your web-worker can work with new request.
You can have a look at most popular backgroung jobs gems for ruby as a starting point: resque
, delayed_jobs
, sidekiq
UPD: Implementation depends on chosen gem, but general scheme will be like this:
# Controller
post '/items' do
# Processing data
MyAwesomeJob.enqueue # here you put your job into queue
head :ok # or whatever
end
In MyAwesomejob
you implement your long-runnning task
Next, about Mongoid and background jobs. You should never use complex objects as job arguments. I don't know what kind of task you are implementing, but there is general answer - use simple objects.
For example, instead of using your User
as argument, use user_id
and then find it inside your job. If you will do it like that, you can use any DB without problems.
Multi-threading ruby workers on heroku
You might try sidekiq, which works similar to resque, but uses threads to process tasks concurrently. You can also use resque and sidekiq together.
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
Instance_Eval Does Not Work with Do/End Block, Only with {}-Blocks
Splitting a String into Words and Punctuation with Ruby
Rails 3.2 Create a Form That's Used in the Footer of Every Page
Quote All Fields in CSV Output
Ruby Block Taking Array or Multiple Parameters
Bundler: Not Executable: Script/Delayed_Job
Determining Method's Visibility on the Fly
How to Fix Ruby Script Which Fails with Encoding Error: "\Xd8" on Us-Ascii
Capture Webcam's Image with Ruby
What Is a More Ruby-Like Way of Doing This Command
Map Array of Ints to Nested Array Access
Shading Mask Algorithm for Radiation Calculations
Does 'Upcase!' Not Mutate a Variable in Ruby
When Should I Use Datetime VS Date, Time Fields in Ruby/Rails
How to Speed Up Ruby/Rake Task
Ruby on Rails - Add Condition on ':Include =>' to Load Limited Number of Objects