lock(){}锁定资源,还是锁定一段代码?

Pre*_*k K 16 c# concurrency multithreading

我还是很困惑......当我们写下这样的东西时:

Object o = new Object();
var resource = new Dictionary<int , SomeclassReference>();
Run Code Online (Sandbox Code Playgroud)

...并且有两个代码块o在访问时锁定resource...

//Code one
lock(o)
{
  // read from resource    
}

//Code two
lock(o)
{
  // write to resource
}
Run Code Online (Sandbox Code Playgroud)

现在,如果我有两个线程,一个线程执行代码读取resource和另一个写入它,我想要锁定resource,当它被读取时,编写器将不得不等待(反之亦然 - 如果它正在写到,读者必须等待).锁构造能帮助我吗?......或者我应该使用别的东西?

(我Dictionary用于此示例的目的,但可以是任何东西)

我特别关注两种情况:

  1. 两个线程试图执行相同的代码行
  2. 两个线程试图在同一资源上工作

lock在两个条件帮助?

Bri*_*sen 12

大多数其他答案都解决了您的代码示例,因此我将尝试在标题中回答您的问题.

锁实际上只是一个标记.拥有令牌的人可以参加舞台.因此,您锁定的对象与您尝试同步的资源没有明确的连接.只要所有读者/作者都同意相同的标记,它就可以是任何东西.

当试图锁定一个对象时(即通过调用Monitor.Enter一个对象),运行时会检查一个线程是否已经锁定了该锁.如果是这种情况,则尝试锁定的线程被挂起,否则它将获取锁定并继续执行.

当持有锁的线程退出锁定范围(即调用Monitor.Exit)时,锁被释放,任何等待的线程现在可以获得锁.

最后关于锁的几点要记住:

  • 只要你需要锁定,但不再锁定.
  • 如果您使用Monitor.Enter/Exit而不是lock关键字,请务必将调用Exit放入finally块中,以便即使在异常情况下也会释放锁定.
  • 暴露对象以锁定使得更难以概述锁定的人和何时锁定.应该封装理想的同步操作.


Var*_*han 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(); }
    }
}
Run Code Online (Sandbox Code Playgroud)

如果有人能想出一个C#代码示例......

  • 如果该资源在其内部代码之外可见,则锁定资源不是一个好主意.这可能导致僵局或竞争条件.建议您锁定私有对象以防止出现该情况. (3认同)