Discourse 中用 Redis 实现互斥锁有这么一段:
if current_expire_time && now <= current_expire_time.to_i
redis.unwatch
got_lock = false
else
result =
redis.multi do
redis.set key, expire_time.to_s
redis.expire key, validity
end
got_lock = !result.nil?
end
当 A 线程还在执行代码时(但是已经超过超时时间),B 线程发现锁已经超过超时时间,然后 B 设置了新的超时时间并获取了锁(仅仅在 A 线程执行完毕后在 log 中记录一条 warning),这种做法是否合理? 感觉上即使一个线程的执行时间如果超过了超时时间,其他线程也不应该获取到这个锁,而是获取锁失败。
另外,也是在 distributed_mutex.rb:
# NOTE wrapped in mutex to maintain its semantics
def synchronize
@mutex.synchronize do
expire_time = get_lock
begin
yield
ensure
current_time = redis.time[0]
if current_time > expire_time
warn("held for too long, expected max: #{@validity} secs, took an extra #{current_time - expire_time} secs")
end
if !unlock(expire_time) && current_time <= expire_time
warn("the redis key appears to have been tampered with before expiration")
end
end
end
end
这里注释说使用 @mutex(Mutex.new)来保持互斥,是为了在下面这种情况下保持互斥吗?
mutex = DistributedMutex.new("foo_key", foo_redis)
10.times do
Thread.new do
mutex.synchronize do
# some code
end
end
end
但是在 Discourse 中的使用方式都是下面这样,所以有点迷惑,这种方式使用的话是不是就不需要 Mutex.new.synchronize 来保证互斥语义了:
DistributedMutex.synchronize("model_#{id}") do
# some code
end
附:Rails 中,关于多线程方面的知识可以在哪里接触到?是一个或 n 个请求就会对应一个线程,又或者是一个 session 会对应一个线程?