Two questions related to Ruby threads
First one:
how can I create a Thread that doesn't start right away.If I use
initialize
without a block an exception gets raised.how can I subclass Thread, so that I may add some custom attributes, but keep the same functionality as the base Thread class? I'd also like 开发者_Go百科to not have to use the
initialize(&block)
method for this.
To better illustrate this:
For the first question:
x = Thread.new
x.run = {
# this should happen inside the thread
}
x.start # i want to manually start the thread
For the second:
x = MyThread.new
x.my_attribute = some_value
x.run = {
# this should happen when the thread runs
}
x.start
I'm looking for something similar to this. Hope you can help.
Question 1
Examining the MRI 1.8.7 source revealed no obvious way to start a thread in the "stopped" state.
What you can do is to have the thread block on a locked mutex, then unlock the mutex when you want the thread to go.
#!/usr/bin/ruby1.8
go = Mutex.new
go.lock
t = Thread.new do
puts "Thread waiting to go"
go.lock
puts "Thread going"
end
puts "Telling the thread to go"
go.unlock
puts "Waiting for the thread to complete"
t.join
# => Thread waiting to go
# => Telling the thread to go
# => Thread going
# => Waiting for the thread to complete
Question 2 (Sort Of)
Did you know you can pass arguments to your thread? Anything passed to Thread.new gets passed through as block arguments:
#!/usr/bin/ruby1.8
t = Thread.new(1, 2, 3) do |a, b, c|
puts "Thread arguments: #{[a, b, c].inspect}"
# => Thread arguments: [1, 2, 3]
end
There are also "thread local variables," a per-thread key/value store. Use Thread#[]=
to set values, and Thread#[]
to get them back. You can use string or symbols as keys.
#!/usr/bin/ruby1.8
go = Mutex.new
go.lock
t = Thread.new(1, 2, 3) do |a, b, c|
go.lock
p Thread.current[:foo] # => "Foo!"
end
t[:foo] = "Foo!"
go.unlock
t.join
Question 2, Really
You can do what you want to do. It's a lot of work, especially when the usual way of handling threads is so simple. You'll have to weigh the plusses and minuses:
#!/usr/bin/ruby1.8
require 'forwardable'
class MyThread
extend Forwardable
def_delegator :@thread, :join
def_delegator :@thread, :[]=
def_delegator :@thread, :[]
def initialize
@go = Mutex.new
@go.lock
@thread = Thread.new do
@go.lock
@stufftodo.call
end
end
def run(&block)
@stufftodo = block
@go.unlock
@thread.join
end
end
t = MyThread.new
t[:foo] = "Foo!"
t.run do
puts Thread.current[:foo]
end
t.join
# => "Foo!"
stuff_to_do = lambda do
# this happens in the thread
end
x = Thread.new( &stuff_to_do )
Ignore the example from Ruby-Doc 1.8.7 because it contains a race condition. See Ruby 2 example, or something like the following:
I tested this in Ruby 2.0 and Ruby 1.8.7, and I found that calling #wakeup
was not enough in 1.8.7' I had to call #run
. The following seems to work in both:
t = Thread.new { Thread.stop; puts "HELLO" }
until t.stop?; end # ensure thread has actually stopped before we tell it to resume
t.run
精彩评论