运行时错误:不能在迭代期间向散列中添加新键

时间:2021-01-02 20:39:17

I get a lot of RuntimeError: can't add a new key into hash during iteration errors in my rails app, but the stack trace doesn't point anywhere interesting.

我得到了很多运行时错误:不能在rails应用程序的迭代错误中向散列添加新键,但是堆栈跟踪没有指向任何有趣的地方。

https://gist.github.com/tal-moshayov/71e06a83cee236fdedd0

https://gist.github.com/tal-moshayov/71e06a83cee236fdedd0

any hint would be highly appreciated

如有任何暗示,我们将不胜感激

1 个解决方案

#1


0  

[add a lock to make shared hash thread-safe]
I faced very similar issue and the problem was that one thread was trying to modify shared object (Hash) while other thread was in the middle of iterating it. Eventually I sorted it out by adding lock (Mutex) and using own Pure-Ruby concurrent Hash.

[添加一个锁以使共享散列线程安全]我遇到了非常相似的问题,问题是一个线程试图修改共享对象(散列),而另一个线程正在迭代它。最后,我通过添加锁(互斥锁)和使用自己的纯ruby并发散列来进行分类。

[examine all threads backtraces to find who owns/iterates your hash]
The problem with the stack trace you get while trying to modify 'busy' hash is that it shows you chain of functions down to the place where you're trying to modify hash, but it does not show who's currently iterating it in parallel (who owns it)! But I found the way to figure that out by printing stack trace for ALL currently running threads. Here's how you do this:

(检查所有线程堆叠找到谁拥有/迭代您的散列)堆栈跟踪的问题你在试图修改“忙”散列是它向您展示了函数链到你想修改的地方散列,但它没有显示谁是当前迭代并行(谁拥有它)!但是我找到了一种方法,通过为所有当前运行的线程打印堆栈跟踪来解决这个问题。你可以这样做:

# This solution was found in comment by @thedarkone on https://github.com/rails/rails/issues/24627
rescue Object => boom

  # only for your particular exception:
  if boom.message == "can't add a new key into hash during iteration"
    thread_count = 0
    Thread.list.each do |t|
      thread_count += 1
      err_msg += "--- thread #{thread_count} of total #{Thread.list.size} #{t.object_id} backtrace begin \n"
      # Lets see if we are able to pin down the culprit
      # by collecting backtrace for all existing threads:
      err_msg += t.backtrace.join("\n")
      err_msg += "\n---thread #{thread_count} of total #{Thread.list.size} #{t.object_id} backtrace end \n"
    end

    # and just print it somewhere you like:
    $stderr.puts(err_msg)

    raise # always reraise
  end

The above code snippet is useful even just for educational purposes as it can show you (like x-ray) how many threads you actually have (versus how many you thought you have - quite often those two are different numbers ;)

上面的代码片段甚至可以用于教育目的,因为它可以向您(如x-ray)显示实际拥有的线程数(与您认为拥有的线程数相比——这两个数字通常是不同的;)

see also: Concurrent::Hash does not lock on method #each

还可以看到:Concurrent:::Hash不会锁定方法#each

#1


0  

[add a lock to make shared hash thread-safe]
I faced very similar issue and the problem was that one thread was trying to modify shared object (Hash) while other thread was in the middle of iterating it. Eventually I sorted it out by adding lock (Mutex) and using own Pure-Ruby concurrent Hash.

[添加一个锁以使共享散列线程安全]我遇到了非常相似的问题,问题是一个线程试图修改共享对象(散列),而另一个线程正在迭代它。最后,我通过添加锁(互斥锁)和使用自己的纯ruby并发散列来进行分类。

[examine all threads backtraces to find who owns/iterates your hash]
The problem with the stack trace you get while trying to modify 'busy' hash is that it shows you chain of functions down to the place where you're trying to modify hash, but it does not show who's currently iterating it in parallel (who owns it)! But I found the way to figure that out by printing stack trace for ALL currently running threads. Here's how you do this:

(检查所有线程堆叠找到谁拥有/迭代您的散列)堆栈跟踪的问题你在试图修改“忙”散列是它向您展示了函数链到你想修改的地方散列,但它没有显示谁是当前迭代并行(谁拥有它)!但是我找到了一种方法,通过为所有当前运行的线程打印堆栈跟踪来解决这个问题。你可以这样做:

# This solution was found in comment by @thedarkone on https://github.com/rails/rails/issues/24627
rescue Object => boom

  # only for your particular exception:
  if boom.message == "can't add a new key into hash during iteration"
    thread_count = 0
    Thread.list.each do |t|
      thread_count += 1
      err_msg += "--- thread #{thread_count} of total #{Thread.list.size} #{t.object_id} backtrace begin \n"
      # Lets see if we are able to pin down the culprit
      # by collecting backtrace for all existing threads:
      err_msg += t.backtrace.join("\n")
      err_msg += "\n---thread #{thread_count} of total #{Thread.list.size} #{t.object_id} backtrace end \n"
    end

    # and just print it somewhere you like:
    $stderr.puts(err_msg)

    raise # always reraise
  end

The above code snippet is useful even just for educational purposes as it can show you (like x-ray) how many threads you actually have (versus how many you thought you have - quite often those two are different numbers ;)

上面的代码片段甚至可以用于教育目的,因为它可以向您(如x-ray)显示实际拥有的线程数(与您认为拥有的线程数相比——这两个数字通常是不同的;)

see also: Concurrent::Hash does not lock on method #each

还可以看到:Concurrent:::Hash不会锁定方法#each