Задача / будущее в Ruby - PullRequest
       1

Задача / будущее в Ruby

16 голосов
/ 21 февраля 2012

Что такое идиоматический аналог Ruby шаблона, который представляет потенциально отложенное асинхронное вычисление с возможностью подписки на его завершение?то есть что-то вроде .NET System.Threading.Task или Python 3.x concurrent.futures.future.

Обратите внимание, что это не обязательно подразумевает многопоточность - фактическая реализация "будущего" объекта будет с такой же вероятностью использоватькакой-то другой способ планирования работы и получения результата, и это выходит за рамки вопроса.Вопрос касается исключительно API, который предоставляется пользователю объекта.

Ответы [ 7 ]

9 голосов
/ 21 февраля 2012

Я не уверен насчет ванильного Ruby, но EventMachine имеет отложений .

Также, ознакомьтесь с этой статьей .

EM.run {
  detector = LanguageDetector.new("Sgwn i os yw google yn deall Cymraeg?")
  detector.callback { |lang| puts "The language was #{lang}" }
  detector.errback { |error| puts "Error: #{error}" }
}
1 голос
/ 15 сентября 2012

Я считаю, что это очень полезно:

https://github.com/wireframe/backgrounded

Это драгоценный камень, который просто позволяет перенести методы в фоновую задачу.

1 голос
/ 02 марта 2012

lazy.rb предоставляет «фьючерсы», но они не совсем такие, как вы описываете (или я ожидал):

Кроме того, библиотека предоставляет фьючерсы, в которых вычисления выполняются немедленно в фоновом потоке.

Таким образом, вы не можете вычислить их позже или вставить значения (возможно, из сети) другими способами.

1 голос
/ 01 марта 2012

Может быть, я что-то упускаю, но если ситуация такая, как вы описали в своем ответе на deepak, то почему бы не обернуть C API в расширение Ruby и предоставить метод Ruby, который принимает блок, соответствующий вашему необходимому обратному вызову? Это также был бы очень идиоматичный Ruby.

Вот пример главы, посвященной расширению Ruby с помощью C из книги «Кирка», обновленной для Ruby 1.9: http://media.pragprog.com/titles/ruby3/ext_ruby.pdf.

Обновление: Вот несколько ссылок, касающихся исключений Ruby в Ruby и в его интерфейсе C.

1 голос
/ 28 февраля 2012

Вы можете использовать очередь заданий, например, resque
. Придумали несколько быстрых примеров для чистого ruby ​​

  1. , разветвив дочерний процесс

    rd, wr = IO.pipe
    
    p1 = fork do
      rd.close
      # sleep is for demonstration purpose only
      sleep 10
      # the forked child process also has a copy of the open file
      # handles, so we close the handles in both the parent and child
      # process
      wr.write "1"
      wr.close
    end
    
    wr.close
    
    puts "Process detaching | #{Time.now}"
    Process.detach(p1)
    puts "Woot! did not block | #{Time.now}"
    
    1.upto(10) do
      begin
        result = rd.read_nonblock(1)
      rescue EOFError
        break
      rescue Exception
        # noop
      end
    
      puts "result: #{result.inspect}"
      system("ps -ho pid,state -p #{p1}")
      sleep 2
    end
    
    rd.close
    
    __END__
    
    ruby 1.9.2p180 (2011-02-18 revision 30909) [x86_64-darwin10.6.0]
    Process detaching | 2012-02-28 17:05:49 +0530
    Woot! did not block | 2012-02-28 17:05:49 +0530
    result: nil
      PID STAT
     5231 S+  
    result: nil
      PID STAT
     5231 S+  
    result: nil
      PID STAT
     5231 S+  
    result: nil
      PID STAT
     5231 S+  
    result: nil
      PID STAT
     5231 S+  
    result: "1"
      PID STAT
    
  2. при наличии обратного вызова в потоке

    require 'thread'
    
    Thread.abort_on_exception = true
    
    module Deferrable
      def defer(&block)
        # returns a thread
        Thread.new do
          # sleep is for demonstration purpose only
          sleep 10
    
          val = block.call
          # this is one way to do it. but it pollutes the thread local hash
          # and you will have to poll the thread local value
          # can get this value by asking the thread instance
          Thread.current[:human_year] = val
          # notice that the block itself updates its state after completion
        end
      end
    end
    
    class Dog
      include Deferrable
      attr_accessor :age, :human_age
      attr_accessor :runner
    
      def initialize(age=nil)
        @age = age
      end
    
      def calculate_human_age_as_deferred!
        self.runner = defer do
          # can do stuff with the values here
          human_age = dog_age_to_human_age
          # and finally publish the final value
          after_defer { self.human_age = human_age }
          # return value of the block. used in setting the thread local
          human_age
        end
      end
    
      protected
      def dog_age_to_human_age
        (self.age / 7.0).round(2)
      end
    
      def after_defer(&block)
        block.call
      end
    end
    
    dog = Dog.new(8)
    dog.calculate_human_age_as_deferred!
    
    1.upto(10) do
      sleep 2
      puts "status: #{dog.runner.status} | human_age: #{dog.human_age.inspect}"
      break unless dog.runner.status
    end
    
    puts "== using thread local"
    
    dog = Dog.new(8)
    dog.calculate_human_age_as_deferred!
    
    1.upto(10) do
      sleep 2
      puts "status: #{dog.runner.status} | human_age: #{dog.runner[:human_year].inspect}"
      break unless dog.runner.status
    end
    
    __END__
    
    ruby 1.9.2p180 (2011-02-18 revision 30909) [x86_64-darwin10.6.0]
    status: sleep | human_age: nil
    status: sleep | human_age: nil
    status: sleep | human_age: nil
    status: sleep | human_age: nil
    status: false | human_age: 1.14
    == using thread local
    status: sleep | human_age: nil
    status: sleep | human_age: nil
    status: sleep | human_age: nil
    status: sleep | human_age: nil
    status: false | human_age: 1.14
    

потребляет меньше памяти, чем разветвление дочернего процесса, но разветвление надежно.Необработанная ошибка в потоке может разрушить всю систему.в то время как необработанная ошибка в дочернем процессе приведет к останову только дочернего процесса

Другие люди указали на наличие волокон и событийную машину (используя EM :: Deferrable и EM.defer) - еще один вариант

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

Eventmachine быстр, но это эксклюзивный мир (например, витой в python).Он имеет свой отдельный стек ввода-вывода, поэтому все библиотеки должны быть написаны для поддержки Eventmachine.Сказав это, я не думаю, что поддержка библиотеки является проблемой для eventmachine

1 голос
/ 21 февраля 2012

Волокно

Волокна - это примитивы для реализации облегченного совместного параллелизма в Ruby. По сути, они являются средством создания блоков кода, которые могут быть приостановлены и возобновлены, как потоки. Основное отличие состоит в том, что они никогда не выгружаются и что планирование должно выполняться программистом, а не виртуальной машиной. ссылка

0 голосов
/ 07 февраля 2014

Может заинтересовать камень thread .Вы можете создать пул потоков, который обрабатывает вещи в фоновом режиме.Gem также поддерживает множество других функций, таких как будущее, задержка и т. Д. Посмотрите на репозиторий github

Похоже, он работает с широким диапазоном версий ruby, а не только с 1.9+, поэтому я использую это

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...