首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >纯Ruby并发哈希

纯Ruby并发哈希
EN

Stack Overflow用户
提问于 2009-07-03 22:05:38
回答 8查看 10.9K关注 0票数 61

实现可以跨多个线程修改但锁数量最少的Hash的最佳方式是什么?出于这个问题的目的,您可以假设Hash将是读取繁重的。它在所有的Ruby实现中都必须是线程安全的,包括那些真正同时操作的实现,比如JRuby,并且它必须用纯Ruby编写(不允许使用C或Java )。

你可以随意提交一个天真的解决方案,但这不太可能是最好的解决方案。为优雅加分,但锁定的可能性较小,胜过较小的代码。

EN

回答 8

Stack Overflow用户

回答已采纳

发布于 2009-07-04 15:54:54

好了,现在您已经指定了“threadsafe”的实际含义,下面是两个可能的实现。下面的代码将永远在MRI和JRuby中运行。无锁实现遵循最终的一致性模型,其中每个线程使用自己的散列视图,如果主线程处于变化中。需要一些小技巧来确保将所有信息存储在线程中不会泄漏内存,但这是经过处理和测试的-运行此代码时,进程大小不会增长。这两个实现都需要更多的工作才能“完成”,这意味着删除、更新等都需要一些思考,但下面两个概念中的任何一个都可以满足您的需求。

对于阅读这个帖子的人来说,认识到整个问题是JRuby独有的是非常重要的-在磁共振成像中,内置的哈希就足够了。

module Cash
  def Cash.new(*args, &block)
    env = ENV['CASH_IMPL']
    impl = env ? Cash.const_get(env) : LocklessImpl
    klass = defined?(JRUBY_VERSION) ? impl : ::Hash
    klass.new(*args)
  end

  class LocklessImpl
    def initialize
      @hash = {}
    end

    def thread_hash
      thread = Thread.current
      thread[:cash] ||= {}
      hash = thread[:cash][thread_key]
      if hash
        hash
      else
        hash = thread[:cash][thread_key] = {}
        ObjectSpace.define_finalizer(self){ thread[:cash].delete(thread_key) }
        hash
      end
    end

    def thread_key
      [Thread.current.object_id, object_id]
    end

    def []=(key, val)
      time = Time.now.to_f
      tuple = [time, val]
      @hash[key] = tuple
      thread_hash[key] = tuple
      val
    end

    def [](key)
    # check the master value
    #
      val = @hash[key]

    # someone else is either writing the key or it has never been set.  we
    # need to invalidate our own copy in either case
    #
      if val.nil?
        thread_val = thread_hash.delete(key)
        return(thread_val ? thread_val.last : nil)
      end

    # check our own thread local value
    #
      thread_val = thread_hash[key]

    # in this case someone else has written a value that we have never seen so
    # simply return it
    #
      if thread_val.nil?
        return(val.last)
      end

    # in this case there is a master *and* a thread local value, if the master
    # is newer juke our own cached copy
    #
      if val.first > thread_val.first
        thread_hash.delete(key)
        return val.last
      else
        return thread_val.last
      end
    end
  end

  class LockingImpl < ::Hash
    require 'sync'

    def initialize(*args, &block)
      super
    ensure
      extend Sync_m
    end

    def sync(*args, &block)
      sync_synchronize(*args, &block)
    end

    def [](key)
      sync(:SH){ super }
    end

    def []=(key, val)
      sync(:EX){ super }
    end
  end
end



if $0 == __FILE__
  iteration = 0

  loop do
    n = 42
    hash = Cash.new

    threads =
      Array.new(10) {
        Thread.new do
          Thread.current.abort_on_exception = true
          n.times do |key|
            hash[key] = key
            raise "#{ key }=nil" if hash[key].nil?
          end
        end
      }

    threads.map{|thread| thread.join}

    puts "THREADSAFE: #{ iteration += 1 }"
  end
end
票数 22
EN

Stack Overflow用户

发布于 2009-07-04 02:23:39

发布基础/朴素的解决方案,只是为了提升我的Stack Overflow cred:

require 'thread'

class ConcurrentHash < Hash
  def initialize
    super
    @mutex = Mutex.new
  end

  def [](*args)
    @mutex.synchronize { super }
  end

  def []=(*args)
    @mutex.synchronize { super }
  end
end
票数 11
EN

Stack Overflow用户

发布于 2009-07-04 02:29:02

耶胡达,我记得你提到依瓦尔的设置是原子的?那么简单的复制和交换如何?

require 'thread'

class ConcurrentHash
  def initialize
    @reader, @writer = {}, {}
    @lock = Mutex.new
  end

  def [](key)
    @reader[key]
  end

  def []=(key, value)
    @lock.synchronize {
      @writer[key] = value
      @reader, @writer = @writer, @reader
      @writer[key] = value
    }
  end
end
票数 7
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/1080993

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档