Два вопроса, связанные с темами Ruby - PullRequest
8 голосов
/ 27 февраля 2010

Первый:

  • как создать поток, который не запускается сразу. Если я использую initialize без блока, возникает исключение.

  • как я могу создать подкласс Thread, чтобы я мог добавить некоторые пользовательские атрибуты, но сохранить те же функциональные возможности, что и базовый класс Thread? Я также хотел бы не использовать метод initialize(&block) для этого.

Чтобы лучше проиллюстрировать это:

По первому вопросу:

x = Thread.new
x.run = {
  # this should happen inside the thread
}
x.start # i want to manually start the thread

Для второго:

x = MyThread.new
x.my_attribute = some_value
x.run = {
  # this should happen when the thread runs
}
x.start

Я ищу что-то похожее на это. Надеюсь, что вы можете помочь.

Ответы [ 3 ]

11 голосов
/ 27 февраля 2010

Вопрос 1

Изучение источника MRI 1.8.7 не выявило очевидного способа запуска потока в состоянии «остановлено».

Что вы можете сделать, это установить блокировку потока на заблокированном мьютексе, а затем разблокировать мьютекс, когда хотите, чтобы поток работал.

#!/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

Вопрос 2 (вроде)

Знаете ли вы, что можете передавать аргументы в свою ветку? Все, что передается в Thread.new, передается как аргументы блока:

#!/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

Есть также «локальные переменные потока», хранилище ключей / значений для каждого потока. Используйте Thread#[]=, чтобы установить значения, и Thread#[], чтобы вернуть их. Вы можете использовать строку или символы в качестве ключей.

#!/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

Вопрос 2, действительно

Вы можете делать то, что хотите. Это большая работа, особенно когда обычный способ обработки потоков настолько прост. Вам придется взвесить плюсы и минусы:

#!/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!"
3 голосов
/ 27 февраля 2010
stuff_to_do = lambda do 
   # this happens in the thread
end

x = Thread.new( &stuff_to_do )
2 голосов
/ 13 февраля 2014

Игнорировать пример из Ruby-Doc 1.8.7 , поскольку он содержит условие гонки. См. Пример Ruby 2 или что-то вроде следующего:

Я проверил это в Ruby 2.0 и Ruby 1.8.7 и обнаружил, что для вызова #wakeup недостаточно в 1.8.7 'Мне пришлось позвонить #run. Следующее, кажется, работает в обоих:

t = Thread.new { Thread.stop; puts "HELLO" }
until t.stop?; end  # ensure thread has actually stopped before we tell it to resume
t.run
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...