我怎么知道在接收和接收消息之间经过了多长时间?

时间:2018-03-16 21:23:00

标签: elixir

这是一个示例流程,可以选择三个特定的初始扑克选项:

defmodule PokerTable do
  # ...more stuff here - in particular, implementations
  # ...  of process* and has_bet functions, state initialization
  # ...  and more irrelevant stuff 

  defp ask_option_on_no_bet(currentTableState) do
    user = currentTableState.currentUser
    newTableState = receive do
      { ^user, :bet, amount } -> process_bet(currentTableState, amount)
      { ^user, :check } -> process_check(currentTableState)
      { ^user, :fold } -> process_fold(currentTableState)
      after 15000 -> process_check(currentTableState)
    end
  end

  defp ask_option_on_bet(currentTableState) do
    user = currentTableState.currentUser
    newTableState = receive do
      { ^user, :raise, amount } -> process_raise(currentTableState, amount)
      { ^user, :call } -> process_check(currentTableState)
      { ^user, :fold } -> process_fold(currentTableState)
      after 15000 -> process_fold(currentTableState)
    end
  end

  def ask_option(currentTableState) do
    if has_bet(currentTableState) do 
        ask_option_on_no_bet(currentTableState)
    else
        ask_option_on_bet(currentTableState)
    end
  end
end

在这种情况下,这个PokerTable只是一个类似于扑克界面的示例模块。那些玩在线扑克网站的人会非常清楚这个界面意味着什么,而且喜欢Elixir的一个理由是开箱即用的能力。

代码的含义是:当新消息到达时,读取内部消息堆栈以查找到达的消息是否与这些子句中的任何一个匹配,并对其进行处理。如果此调用后15秒后未收到匹配的消息,则执行默认操作

这种方法虽然诱人,但有一点需要注意:不匹配的消息将保留在内部流程中。消息框(这是一个FIFO),这意味着两件事:

  • 您现在可以通过发送无效消息吃掉我的记忆,这些消息无法处理,将保留在包装盒中。
  • 有问题的(或者是magicius)客户端可能会为特定用户发送转弯消息(现在没有转向!)。这些消息将在用户转向时生效,并将被处理。

然后我可以为receive块阻止以下方法:

  defp ask_option_on_no_bet(currentTableState) do
    user = currentTableState.currentUser
    newTableState = receive do
      { ^user, :bet, amount } -> process_bet(currentTableState, amount)
      { ^user, :check } -> process_check(currentTableState)
      { ^user, :fold } -> process_fold(currentTableState)
      anyJunk ->
        tell_the_user_they_sent_junk(user)
        ask_option_on_no_bet(currentTableState)
      after 15000 -> process_check(currentTableState)
    end
  end

  defp ask_option_on_bet(currentTableState) do
    user = currentTableState.currentUser
    newTableState = receive do
      { ^user, :raise, amount } -> process_raise(currentTableState, amount)
      { ^user, :call } -> process_check(currentTableState)
      { ^user, :fold } -> process_fold(currentTableState)
      anyJunk ->
        tell_the_user_they_sent_junk(user)
        ask_option_on_bet(currentTableState)
      after 15000 -> process_fold(currentTableState)
    end
  end

现在我避免在邮件队列中存储垃圾。到现在为止还挺好。但是,这会导致对receive的新来电,重置计时器,因为after会在当前receive来电后运行。

我的想法是:

  • 检索receive和垃圾命令之间当前经过的毫秒数。
  • 减去15000 - theRetrievedElapsedMiliseconds并将该值保留为州的成员(在这种情况下我们会更新,并且我们会在ask_option上初始化为15000)。
  • 而不是after 15000,在remainingTime = currentTableState.remainingTime块中使用两行after remainingTimereceive(并在currentTableState中更新remainingTime,生成新状态为尾调用。)

我的问题是:如何检索自receive以来经过的时间?

1 个答案:

答案 0 :(得分:1)

我会这样做:

  1. 向函数添加第二个参数,默认值为15000,即总超时。

  2. 当功能启动时,存储当前时间。

  3. 当它进入any_junk分支时,减去剩余时间,然后进行递归调用。

  4. defp ask_option_on_no_bet(currentTableState, remaining \\ 15000) do
      start = now()
      user = currentTableState.currentUser
      newTableState = receive do
        { ^user, :bet, amount } -> ...
        { ^user, :check } -> ...
        { ^user, :fold } -> ...
        any_junk ->
          elapsed = now() - start
          ask_option_on_no_bet(currentTableState, remaining - elapsed)
        after remaining ->
          process_check(currentTableState)
      end
    end
    

    now()只是一个简单的函数,以毫秒为单位返回当前的单调时间:

    defp now, do: System.monotonic_time(:millisecond)