【问题标题】:How to kill Elixir process after timeout如何在超时后杀死 Elixir 进程
【发布时间】:2016-04-22 15:09:45
【问题描述】:

如何启动 Elixir 进程,然后在超时后将其终止?我有这样的代码:

defmodule OperationsManager do
  def run_operation do
    spawn fn -> 
      # long operation 
    end
  end
end

内部操作可能持续时间太长而无法等待其结束,因此我需要从管理器中终止该进程。我该怎么做?

编辑:

重要细节:我需要生成几个操作,并且所有操作都应该有单独的超时。

【问题讨论】:

    标签: elixir


    【解决方案1】:

    为了完整性:erlang :timer 模块有一个函数kill_after 会在时间过去后强制终止进程。

    # Kill the current process after 2 seconds 
    :timer.kill_after(:timer.seconds(2))
    
    # Kill pid after 2 minutes
    :timer.kill_after(:timer.minutes(2), pid)
    

    【讨论】:

    • 这应该是答案!
    【解决方案2】:

    我会看看 Elixir Task module

    您可以使用Task.yield 或多个任务Task.yield_many 在给定的时间间隔内处理单个任务,尽管Task.yield 似乎更接近您可能需要的内容。 Yield 返回{:ok, result}(成功返回)、{:exit, reason}(任务崩溃)或:nil(超过超时间隔)。 您还可以考虑将任务放在监督树中。

    以下代码基于elixir 1.2.1。

    defmodule OperationsManager do
      def run_operation() do
    
        task1 = Task.async(fn() -> operation("task 1", 1) end)
        result = Task.yield(task1, 5000)
        process_task(task1, result)
    
        task2 = Task.async(fn() -> operation("task 2", 2) end)
        task4 = Task.async(fn() -> operation("task 4", 4) end)
        task6 = Task.async(fn() -> operation("task 6", 6) end)
        task8 = Task.async(fn() -> operation("task 8", 8) end)
    
        results = Task.yield_many([task2, task4, task6, task8], 7000)
        for {task, res} <- results do
          process_task(task, res)
        end
    
      end
    
      def process_task(task, res) do
        case res do
          :nil ->
            IO.write("shutting down timed out task: ")
            IO.inspect(task)
            Task.shutdown(task, :brutal_kill)
          {:ok, task_number} ->
            IO.puts("#{task_number} is complete")
          {:exit, _reason} ->
            # some logic when the task terminates
        end
      end
    
      def operation(task_number, timeout) do
        :timer.sleep(timeout * 1000)
        task_number
      end
    
    end
    
    
    OperationsManager.run_operation()
    

    【讨论】:

    • 我刚刚开始学习 Elixir,所以也许我会有足够多的问题来提高你在 SO 的声誉:)
    【解决方案3】:

    您可以使用在进程之间发送消息来管理各种情况,例如:

    parent_pid = self()
    spawn_pid = spawn fn -> 
      # some code...
      # :timer.sleep(1_500)
      send(parent_pid, {:done, self()})
    end
    # wait a result from spawned proccess
    receive do
      {:done, pid} -> "Got :done from #{inspect pid}"
    after # 1s timeout
      1_000 -> Process.exit(spawn_pid, :kill)
    end
    # ensure that spawned process is finished or killed
    Process.alive?(spawn_pid) # => false
    

    【讨论】:

    • 不过只有一个操作的时候才合适,不是吗?
    • 什么意思?每个衍生的操作都将返回一个 PID。因此,您可以随心所欲地使用它们。
    • 我的意思是,如果我在不同时间产生两个操作,receive 构造如何区分一个操作的超时和另一个操作的超时?但也许我错过了一些观点。
    • 这个话题太大了,没法评论。所以我推荐阅读这篇文章:theerlangelist.com/article/beyond_taskasync
    猜你喜欢
    • 2012-05-30
    • 2011-07-06
    • 1970-01-01
    • 2021-07-30
    • 2013-07-24
    • 2012-05-03
    • 2019-12-09
    • 1970-01-01
    • 2010-12-08
    相关资源
    最近更新 更多