Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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 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.

like image 350
Geo Avatar asked Feb 27 '10 10:02

Geo


2 Answers

stuff_to_do = lambda do 
   # this happens in the thread
end

x = Thread.new( &stuff_to_do )
like image 24
Farrel Avatar answered Oct 01 '22 20:10

Farrel


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!"
like image 154
Wayne Conrad Avatar answered Oct 01 '22 22:10

Wayne Conrad