I am new to ruby and trying to work around threads
Let's say I have a method which I want to run every x seconds as follows
def say_hello
puts 'hello world'
end
I am trying to run it as follows
Thread.new do
while true do
say_hello
sleep(5)
end
end
But when I run the script, nothing is displayed on the console. What am I missing? Thanks!
The main thread is exiting before your thread can run. Use the join method to make the current thread wait for the say_hello thread to finish executing (though it never will).
t = Thread.new do
while true do
say_hello
sleep(5)
end
end
t.join
You are creating the Thread object, but you are not waiting for it to finish its execution, try with:
Thread.new do
while true do
say_hello
sleep(5)
end
end.join
Try
t1 = Thread.new do
while true do
say_hello
sleep(5)
end
end
t1.join
Related
my code here...
require 'thread'
$temp = Thread.new do
loop do
puts 'loop me'
begin
puts "try thread"
raise Exception.new('QwQ') if rand > 0.5
puts "skip try"
rescue
puts "QwQ"
end
sleep(0.5)
end
puts '...WTF'
end
loop do
puts "runner #{Thread.list.length} #{$temp.status}"
sleep(2)
end
how to keep runner and loop thread running? and how to fix it like this code?
I tried like Thread.abort_on_exception , but it will kill the process...
Catch the exception inside the thread, and set the error in a variable accessible by the main thread (for testing you could use a global variable like so: $thread_error).
If the error-variable exists, then raise it from the main thread.
You could also use a queue to communicate between the threads, but then it wouldn't be able to utilize multiple threads.
require 'thread'
$temp = Thread.new do
begin
loop do
puts 'loop me'
begin
puts "try thread"
raise Exception.new('QwQ') if rand > 0.5
puts "skip try"
rescue
puts "QwQ"
end
sleep(0.5)
end
puts '...WTF'
rescue Exception => e
$thread_error = e
raise e
end
end
loop do
puts "runner #{Thread.list.length} #{$temp.status}"
raise $thread_error if $thread_error
sleep(2)
end
I am writing a Ruby application (Ruby v2.1.3p242 in Linux x86_64) that will repeatedly process online data and store the results in a database. To speed things up, I have multiple threads running concurrently and I have been working on a way to cleanly stop all the threads both on command and when an exception is raised from a thread.
The issue is that some threads will continue to run multiple iterations of #do_stuff after Sever.stop is called. They do eventually stop, but I will see a couple threads running 10-50 times after the rest have stopped.
Each threads' mutex is locked before each iteration and unlocked afterwards. The code, #mutex.synchronize { kill } is run on each thread when Server.stop is called. This should kill the thread immediately after its next iteration, but this does not seem to be the case.
EDIT:
The code works as-is, so feel free to test it if you like. In my tests, it takes between 30 seconds and several minutes for all of the threads to stop after calling Server.stop. Note that each iteration takes between 1-3 seconds. I used the following to test the code (using ruby -I. while in the same directory):
require 'benchmark'
require 'server'
s = Server.new
s.start
puts Benchmark.measure { s.stop }
Here is the code:
server.rb:
require 'server/fetcher_thread'
class Server
THREADS = 8
attr_reader :threads
def initialize
#threads = []
end
def start
create_threads
end
def stop
#threads.map {|t| Thread.new { t.stop } }.each(&:join)
#threads = []
end
private
def create_threads
THREADS.times do |i|
#threads << FetcherThread.new(number: i + 1)
end
end
end
server/fetcher_thread.rb:
class Server
class FetcherThread < Thread
attr_reader :mutex
def initialize(opts = {})
#mutex = Mutex.new
#number = opts[:number] || 0
super do
loop do
#mutex.synchronize { do_stuff }
end
end
end
def stop
#mutex.synchronize { kill }
end
private
def do_stuff
debug "Sleeping for #{time_to_sleep = rand * 2 + 1} seconds"
sleep time_to_sleep
end
def debug(message)
$stderr.print "Thread ##{#number}: #{message}\n"
end
end
end
There's no guarantee that the thread calling stop will acquire the mutex before the next iteration of the loop. It's totally up to the Ruby and operating system schedulers, and some OSes (including Linux) don't implement a FIFO scheduling algorithm, but take other factors into account to try to optimize performance.
You can make this more predictable by avoiding kill and using a variable to exit the loop cleanly. Then, you only need to wrap the mutex around the code that accesses the variable
class Server
class FetcherThread < Thread
attr_reader :mutex
def initialize(opts = {})
#mutex = Mutex.new
#number = opts[:number] || 0
super do
until stopped?
do_stuff
end
end
end
def stop
mutex.synchronize { #stop = true }
end
def stopped?
mutex.synchronize { #stop }
end
#...
end
end
I make some important calculations in endless loop and don't want this calculation interrupts with SIGINT signal (e.g. ctrl-c). So I place loop in thread with protecting important calculation with mutex:
mutex = Mutex.new
trap('INT') do
Thread.new do
puts 'Terminating..'
exit(0)
end.join
end
Thread.new do
loop do
mutex.synchronize do
puts 'Some important computation is started.'
sleep(5)
puts 'Some important computation is done.'
end
sleep(30)
end
end.join
I add another thread inside trap block, so I expect this thread will be executed only when mutex will be unlocked.
But in fact, this second thread starts immediately after receiving SIGINT signal:
Some important computation is started.
^CTerminating..
What am I missed/doing wrong?
You must synchronize the trap thread with the computation:
trap('INT') do
Thread.new do
mutex.synchronize do
puts 'Terminating..'
exit(0)
end
end.join
end
But perhaps it is easier if you set a boolean var in your trap function, and you use it to break the loop.
mustexit = false
trap('INT') do
mustexit= true
end
Thread.new do
loop do
puts 'Some important computation is started.'
sleep(5)
puts 'Some important computation is done.'
if mustexit then
break
end
end
sleep(30)
end.join
I have a thread in Ruby. It runs a loop. When that loop reaches a sleep(n) it halts and never wakes up. If I run the loop with out sleep(n) it runs as a infinite loop.
Whats going on in the code to stop the thread from running as expected?
How do i fix it?
class NewObject
def initialize
#a_local_var = 'somaText'
end
def my_funk(a_word)
t = Thread.new(a_word) do |args|
until false do
puts a_word
puts #a_local_var
sleep 5 #This invokes the Fail
end
end
end
end
if __FILE__ == $0
s = NewObject.new()
s.my_funk('theWord')
d = gets
end
My platform is Windows XP SP3
The version of ruby I have installed is 1.8.6
You're missing a join.
class NewObject
def initialize
#a_local_var = 'somaText'
end
def my_funk(a_word)
t = Thread.new(a_word) do |args|
until false do
puts a_word
puts #a_local_var
sleep 5
end
end
t.join # allow this thread to finish before finishing main thread
end
end
if __FILE__ == $0
s = NewObject.new()
s.my_funk('theWord')
d = gets # now we never get here
end
In the situation below the #crawl object DOES RECEIVE the crawl call, but the method mock fails ie: the method is not mocked.
Does Thread somehow create its own copy of the #crawl object escaping the mock?
#crawl.should_receive(:crawl).with(an_instance_of(String)).twice.and_return(nil)
threads = #crawl.create_threads
thread creation code:
def crawl(uri)
dosomecrawling
end
def create_threads
(1..5).each do
Thread.new do
crawl(someurifeedingmethod)
end
end
end
It does not appear from the code posted that you are joining the threads. If so, there is a race condition: Sometimes the test will execute with some or all of the threads not having done their job; The fix is along these lines:
!/usr/bin/ruby1.9
class Crawler
def crawl(uri)
dosomecrawling
end
def create_threads
#threads = (1..5).collect do
Thread.new do
crawl(someurifeedingmethod)
end
end
end
def join
#threads.each do |thread|
thread.join
end
end
end
describe "the above code" do
it "should crawl five times" do
crawler = Crawler.new
uri = "uri"
crawler.should_receive(:someurifeedingmethod).with(no_args).exactly(5).times.and_return(uri)
crawler.should_receive(:crawl).with(uri).exactly(5).times
crawler.create_threads
crawler.join
end
end
This code works perfectly.
You can add 5 times the expects.
class Hello
def crawl(uri)
puts uri
end
def create_threads
(1..5).each do
Thread.new do
crawl('http://hello')
end
end
end
end
describe 'somting' do
it 'should mock' do
crawl = Hello.new
5.times do
crawl.should_receive(:crawl).with(an_instance_of(String)).and_return(nil)
end
threads = crawl.create_threads
end
end