锁定C#代表进行写入是否能保证线程安全

本文关键字:是否 安全 线程 锁定 | 更新日期: 2023-09-27 18:14:22

C#委托是不可变的,因此当您从委托中添加或删除函数时,它会被一个新对象替换。

考虑以下代码:

lock(shared_instance.my_delegate){
  shared_instance.my_delegate+=Foo;
}

考虑以下情况:

线程1和2到达锁,线程2阻塞委托实例,我们称之为A

线程1用新实例B替换A,并退出锁。

线程2是在B上还是在A上获得锁定?如果它在A上,那么在我看来,线程3可以出现,在线程2获取A上的锁的同时获取B上的锁,并且他们可以同时尝试覆盖删除,并且会发生竞争,对吗?

编辑:

我现在意识到这个问题可以推广到任何参考类型:

lock(shared_ref){
  shared_ref=different_ref;
}

基本上我想知道的是:如果发生这种情况,等待线程仍然会锁定旧引用,是吗?

锁定C#代表进行写入是否能保证线程安全

查看以下代码:

lock(shared_ref){
  shared_ref=different_ref;
}

lock(shared_ref)将锁定shared_ref变量内的引用,替换变量的值不会改变锁定,并且在lock { }块结束时,旧引用将被释放。因此,如果任何其他线程在shared_ref被更改之前锁定了它,但在它被锁定时,该锁定仍然会被释放。当新引用已经设置好时,第三个线程可能会锁定到它上,并且会发生对变量设置的竞争,因为线程2将被释放,而线程3从不在锁定处等待,因为没有人持有该引用。因此线程2和3将竞相设置新的变量

我刚刚做了一些示例代码,我认为它非常清楚:

public class TestClass
{
    public static object myObject;
    public static void setObject(object newValue, string thread)
    {
        lock(myObject)
        {
            Debug.Print(thread+" reached the inside of the lock");
            Thread.Sleep(1000);
            myObject = newValue;
            Debug.Print(thread + " myObject was set");
            Thread.Sleep(1000);
        }
        Debug.Print(thread + " lock released");
    }
    public static void Test()
    {
        myObject = new object();
        Thread t1 = new Thread(t1_run);
        Thread t2 = new Thread(t2_run);
        Thread t3 = new Thread(t3_run);
        t1.Start();
        t2.Start();
        t3.Start();
    }
    private static void t1_run()
    {
        setObject(new object(), "t1");
    }
    private static void t2_run()
    {
        Thread.Sleep(500); // 500 millisecods so it will be locked on the old
        setObject(new object(), "t2");
    }
    private static void t3_run()
    {
        Thread.Sleep(1500); // just make sure myObject was replaced
        setObject(new object(), "t3");
    }
}

现在明显的输出是:

t1 reached the inside of the lock
t1 myObject was set
t3 reached the inside of the lock
t1 lock released
t2 reached the inside of the lock
t3 myObject was set
t2 myObject was set
t3 lock released
t2 lock released

因为睡眠确保了t2和t3设置myObject的顺序。但当时间非常接近时,这是无法确保的

线程2在B上还是在A上获得锁定?如果它在A上,那么在我看来,线程3可以出现,在线程2获取A上的锁的同时获取B上的锁,他们可以同时尝试覆盖删除,然后会发生竞争,对吗?

在这两种情况下,你都有种族状况。代码中有一个竞赛条件,如果使用专用对象锁定,则有竞赛条件;如果根本没有lock,则有相同的竞赛条件。在所有这三种情况下,其中一个线程将设置值并立即覆盖它,另一个线程则将第二次设置值并使其保持不变。在所有这些情况下,哪个线程将"获胜"是不确定的。在这些情况下,除了将值设置为两个可能值之一之外,不会发生任何其他事情。