将Ruby Thread的结果作为参数传递给方法

问题描述 投票:0回答:1

我有这些控制器方法

def student_progress_variables(student_email)
    dashboard = Dashboard.new
    @projects = Thread.new { dashboard.obtain_projects }

    @current_student = obtain_student_with_email(student_email)

    @reviews = obtain_selected_student_project_reviews(@current_student)
    @requests = obtain_student_code_review_requests(@current_student).sort_by do |request|
      request[obtain_code_review_requests_id]
    end

    @review_completions = obtain_student_code_review_completions(@current_student)
    @courses = obtain_projects_courses(@projects.join)
  end

我想将@projects的响应传递给@courses方法调用,但我一直得到这个undefined method 'each' for #<Thread:0x00007f30dc83da10>错误。我已阅读文档但无法取得任何进展。有任何想法吗?

编辑:我选择@max pleaner的建议。以下是代码段的当前状态。我已经看到了更好的结果:

def student_progress_variables(student_email)
    thread1 = Thread.new do 
      @current_student = obtain_student_with_email(student_email)
    end
    thread2 = Thread.new do 
      dashboard = Dashboard.new
      @projects = dashboard.obtain_projects
      @courses = obtain_projects_courses(@projects)
    end
    thread1.join

    thread3 = Thread.new do 
      @reviews = obtain_selected_student_project_reviews(@current_student)
    end

    @requests = obtain_student_code_review_requests(@current_student).sort_by do |request|
      request[obtain_code_review_requests_id]
    end
    @review_completions = obtain_student_code_review_completions(@current_student)
    thread2.join
    thread3.join
  end
ruby multithreading ruby-on-rails-5.1
1个回答
1
投票

线程没有返回值(Thread对象本身除外)。

这是javascript中一个非常常见的问题,在这里您使用许多异步函数(例如setTimeout),这些函数不会像同步函数那样生成返回值。

Javascript通常用来处理这个问题的是回调(以及promises / async / await)。您可以以块的形式在ruby中使用回调:

  class AsyncClass
    attr_reader :foo, :bar
    def async_method(&callback)
      Thread.new do
        @foo = 1
        @bar = @foo + 1
        callback.call(self)
      end
    end
  end

虽然你可以在任何线程上调用join来暂停代码执行直到它完成,但这样就完全消除了使用线程的目的(除非你正在进行并行处理)。因此,任何调用async_method的方法都需要本身异步。

这不会像控制器动作那样工作,这是同步的(除非你使用流响应或服务器推送,但我会假设你不是)。

它是异步代码的一种“法则”,你不能从同步函数调用异步函数,并获得'返回值',而不调用join(并强制它同步运行)。因此,无论何时需要异步函数的“返回值”,都需要使调用函数为异步,并使用块/回调来读取结果。请注意,这个恼人的过程有时在javascript中被称为“回调地狱”,这就是为什么他们实现了promises / async / await(顺便说一下,它似乎得到了ruby-concurrency gem的支持)。

但是,假设您从另一个异步方法调用此方法:

  class OtherClass
    def initialize
      AsyncClass.new.async_method do |async_class_inst|
        puts async_class_inst.bar
      end
      sleep 1
    end
  end

  OtherClass.new
  # prints 2

在你的情况下,它看起来像这样:

  def student_progress_variables(student_email, &blk)
    dashboard = Dashboard.new
    Thread.new do
      @projects = dashboard.obtain_projects
      @current_student = obtain_student_with_email(student_email)

      @reviews = obtain_selected_student_project_reviews(@current_student)
      @requests = obtain_student_code_review_requests(@current_student).sort_by do |request|
        request[obtain_code_review_requests_id]
      end

      @review_completions = obtain_student_code_review_completions(@current_student)
      @courses = obtain_projects_courses(@projects.join)
      blk.call(self)
    end
  end

但是,再次,您只能从另一个异步方法调用它:

  class AsyncCaller
    def initialize(&callback)
      SomeClass.new.student_progress_variables("[email protected]") do |some_class_inst|
        callback.call(self, some_class_inst)
      end
      sleep 1
    end
  end

  AsyncCaller.new do |async_caller_inst, some_class_inst|
    # .... do things here, thread is done
  end

请注意,在这些示例中,我将self传递给回调,以便将其分配给块变量(例如async_class_inst)。这是因为self的值会根据您调用块的位置而更改,如您在此示例中所示:

  class A
    def initialize(&blk)
      blk.call
      sleep 1 
    end
  end

  A.new { puts self }
  # prints 'main'

所以,如果你在线程中对self进行一些操作(就像你一样,通过设置实例变量),最好将self显式传递给回调/块,这样你就不必假设调用者有了否则就可以访问它。

另外,请不要在你的代码中放入sleep调用,我只使用这些调用,所以如果你将代码片段作为脚本运行它就可以了。在实际代码中,如果要等到线程完成,则应使用Thread#join

重申一下,如果您希望及时将结果包含在响应中,则不应在控制器操作中使用线程(除非您最后使用.join进行并行处理)。

© www.soinside.com 2019 - 2024. All rights reserved.