锁定C#代理是否可以保证线程安全?

时间:2015-09-24 17:13:27

标签: c# multithreading delegates

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

考虑以下代码:

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

考虑以下情况:

线程1和2到达锁定,委托实例上的线程2块,让我们称之为A

线程1将A替换为新实例B,然后退出锁定。

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

编辑:

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

lock(shared_ref){
  shared_ref=different_ref;
}

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

2 个答案:

答案 0 :(得分:1)

  

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

在这两种情况下你都有竞争条件。您的代码存在竞争条件,如果使用专用对象锁定,则具有竞争条件,如果您根本没有lock,则具有相同的竞争条件。在所有这三种情况下,其中一个线程将设置该值并立即覆盖它,并且将一个值设置为第二个并使其保持不变。在所有这些情况下,哪个线程将“赢”是不确定的。在这些情况下,除了将值设置为两个可能值之一之外,其他任何情况都不会发生。

答案 1 :(得分:1)

查看此代码:

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的顺序。但是,当时间非常接近时,这并不能确保