我仍然感到困惑......当我们写下这样的事情时:
Object o = new Object();
var resource = new Dictionary<int , SomeclassReference>();
...并且有两个代码块可以在访问o
...
resource
//Code one
lock(o)
{
// read from resource
}
//Code two
lock(o)
{
// write to resource
}
现在,如果我有两个线程,一个线程执行的代码从resource
读取而另一个写入它,我想锁定resource
,这样当它被读取时,写入者将不得不等待(反之亦然 - 如果它被写入,读者将不得不等待)。锁构造能帮助我吗? ......或者我应该使用其他东西吗?
(我在本例中使用Dictionary
,但可以是任何内容)
我特别关注两种情况:
lock
会在这两种情况下提供帮助吗?
答案 0 :(得分:12)
其他大多数答案都会解决您的代码示例,因此我会尝试在标题中回答您的问题。
锁实际上只是一个标记。拥有令牌的人可以参加舞台。因此,您锁定的对象与您尝试同步的资源没有明确的连接。只要所有读者/作者都同意相同的标记,它就可以是任何东西。
当试图锁定某个对象时(即通过调用某个对象上的Monitor.Enter
),运行时会检查该锁是否已被某个线程持有。如果是这种情况,则尝试锁定的线程被挂起,否则它将获取锁定并继续执行。
当持有锁的线程退出锁定范围(即调用Monitor.Exit
)时,锁被释放,任何等待的线程现在都可以获得锁。
最后关于锁的几点要记住:
Monitor.Enter/Exit
代替lock
关键字,请确保将Exit
的号码置于finally
块中,以便即使在案件中也会释放锁定一个例外。 答案 1 :(得分:5)
是的,使用锁是正确的方法。您可以锁定任何对象,但正如其他答案所述,锁定资源本身可能是最简单和最安全的。
但是,您可能希望使用读/写锁对而不是单个锁,以减少并发开销。
这样做的理由是,如果你只有一个线程写入,但是几个线程读取,你不希望读取操作阻止其他读操作,而只是读取块写入,反之亦然。
现在,我更像是一个java人,所以你必须改变语法并挖掘一些文档以在C#中应用它,但是rw-locks是Java中标准并发包的一部分,所以你可以写点如下:
public class ThreadSafeResource<T> implements Resource<T> {
private final Lock rlock;
private final Lock wlock;
private final Resource res;
public ThreadSafeResource(Resource<T> res) {
this.res = res;
ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
this.rlock = rwl.readLock();
this.wlock = rwl.writeLock();
}
public T read() {
rlock.lock();
try { return res.read(); }
finally { rlock.unlock(); }
}
public T write(T t) {
wlock.lock();
try { return res.write(t); }
finally { wlock.unlock(); }
}
}
如果有人能提出C#代码示例......
答案 2 :(得分:2)
这两个代码块都锁定在这里。如果线程1锁定第一个块,而线程2尝试进入第二个块,则必须等待。
答案 3 :(得分:2)
lock(o){...} 语句编译为:
Monitor.Enter(o)
try { ... }
finally { Monitor.Exit(o) }
如果另一个线程已经调用了,那么对Monitor.Enter()的调用将阻塞该线程。只有在其他线程在对象上调用Monitor.Exit()之后才会解除阻塞。
答案 4 :(得分:2)
在两种情况下都会锁定帮助吗? 是。
lock(){}会锁定资源吗? 它锁定了一段代码?
lock(o)
{
// read from resource
}
是
的语法糖Monitor.Enter(o);
try
{
// read from resource
}
finally
{
Monitor.Exit(o);
}
Monitor 类包含用于同步对代码块的访问的对象集合。 对于每个同步对象,监控保留:
因此,当一个线程调用lock(o)时,它被置于o的就绪队列中,直到它被锁定为o,此时它继续执行其代码。
答案 5 :(得分:0)
假设您只涉及一个流程,这应该可行。如果您希望在多个流程中工作,则需要使用“Mutex”。
哦,而且“o”对象应该是一个单例或者需要锁定的所有区域,因为真正被锁定的是该对象,如果你创建一个新对象,那么新的对象将不会被锁定然而。
答案 6 :(得分:0)
实施它的方式是一种可接受的方式来完成您需要做的事情。改进你的方法的一种方法是在字典本身上使用lock(),而不是用于同步字典的第二个对象。这样,资源本身不会传递额外的对象,而是跟踪它自己的监视器上是否存在锁定。
在某些情况下,使用单独的对象可能很有用,例如同步对外部资源的访问,但在这种情况下,它会产生开销。