C# 在并发调用方法时,是否需要同步访问方法参数和局部变量?

C# 在并发调用方法时,是否需要同步访问方法参数和局部变量?,c#,concurrency,thread-safety,locking,C#,Concurrency,Thread Safety,Locking,我已经用静态类/方法编写了很多代码,我相信这些静态类/方法将由多个线程同时调用/执行。所以我在我的方法中做了很多锁定。我通常会这样做: public static class MyThreadsafeMethods { private static Object staticLock1 = new Object(); private static Object staticLock2 = new Object(); public static string Stati

我已经用静态类/方法编写了很多代码,我相信这些静态类/方法将由多个线程同时调用/执行。所以我在我的方法中做了很多锁定。我通常会这样做:

public static class MyThreadsafeMethods {

    private static Object staticLock1 = new Object();
    private static Object staticLock2 = new Object();

    public static string StaticMethod1(string param1, int param2) {
        lock (staticLock1) {
            var _param1 = param1;
            var _param2 = param2;

            //highly confidential business logic here

            return StaticMethod2(_param1, "Integer: " + _param2.ToString());
        }
    }

    public static string StaticMethod2(string param1, string param2) {
        lock (staticLock2) {
            var _param1 = param1;
            var _param2 = param2;

            //truly groundbreaking algorithm here

            return _param1 + " - " + _param2;
        }
    }
}
我想知道两件事:

1) 我认为我需要在“锁定代码”中使用参数的本地“副本”;因为如果另一个线程使用param1和param2的不同值调用我的方法,这可能会扰乱我的处理。如果我只使用在锁定代码中声明/实例化的变量(即上面示例中的_param1和_param2),那么可能会更改param1和param2的值(或发送对不同对象的引用),我也可以。但我需要这样做吗?我是不是太偏执了

2) 我决定在需要锁对象之前不要实例化它们,因为我收集的静态锁对象正在增加。。。所以,我现在要做的是:

    private static Object staticLock1;

    public static string StaticMethod1(string param1, int param2) {
        lock(staticLock1 = staticLock1 ?? new Object()) {
            (...)
        }
    }

是否有任何理由认为在我第一次需要锁对象时实例化它是不安全的?在lock语句中对lock对象使用赋值运算符会不会导致问题或阻止对象正确锁定?

首先:创建对象实例的开销非常小。不要担心,除非测量结果表明你应该这样做

只需这样初始化:

private readonly static object staticLock1 = new Object();
使用lock语句的方式是不安全的

第二:我在方法中没有看到共享数据,因此没有理由锁定

最后:如果它包含许多静态函数,我会重新考虑设计

  • 你所拥有的不会有任何区别-参数本身在调用后无法更改,因此它实际上没有任何作用。在使用字符串的情况下,可以非常安全地看到字符串是不可变的。如果情况并非如此,那么传递的任何内容都有可能在其他地方被更改。在这种情况下,您必须制作一份真实的副本(即,不只是复制引用)

  • 考虑两个线程同时到达
    lock(staticLock1=staticLock1??newobject())
    的情况。它们都可以将
    staticLock1
    视为null。所以不,那不安全


  • 您的主要困惑似乎是为了同时安全地调用静态方法,您需要什么样的同步

    由于多个线程以不同步的方式访问同一存储位置,并且其中至少有一个线程是写入线程,因此总是会出现数据争用。这个基本规则足以解释许多并发性问题

    调用方法时,每个调用的参数都有不同的存储位置。他们是独立的。因此,调用同一方法的两个线程永远不会竞相访问方法参数。因此,您永远不需要同步对方法参数的访问

    当地人也是如此。事实上,参数具有与局部变量相同的同步属性。它们存在于每次通话中


    回答第二个问题:这是不安全的,因为两个线程可能会锁定不同的对象。此外,还可以在多个线程上不同步地写入
    staticLock1
    。我已经解释过这是一场数据竞赛。

    你可以使用
    静态只读对象
    我想我从来没有想过在我的锁对象上使用只读,因为我从未将它们重新分配给新的/不同的对象。但是使用它不会特别阻止我做我想做的事情吗?我试图在锁定该对象的第一个静态方法中仅实例化lock对象;并尽量减少要键入的步骤数/代码量。很抱歉,我上面的示例代码可能与我的实际代码不完全匹配。我确信我的情况下确实需要锁定;我已经证明,如果没有它,我的应用程序会出现问题。你知道我建议的在第一次需要锁对象时实例化它的代码是否会有问题吗?在staticLock1上锁定的第二种方法比第一种方法的线程安全性低吗?在lock语句中测试和分配的方法是错误的。试想一下,如果在测试之后和锁定之前,另一个线程执行相同的测试,会发生什么情况。除了这样做,每次锁定时都会执行此测试。谢谢Chris。我相信你完全回答了我的两个问题。我只想对一件事做一个额外的肯定——考虑一下这个场景:线程1调用StaseMedith1(“TestStand”,12)。因此,StaticMethod1的“锁定”处理从param1=“teststring”和param2=12开始。现在,线程2调用StaticMethod1(“newstring”,10)。我只想确认,在处理StaticMethod1(由线程1调用)的过程中,param1没有设置为“newstring”。我很感谢你提供的信息,我只是想确保我正确理解了你的答案。线程有自己的上下文/状态。对于每个线程,局部变量(参数是)是完全独立的。当多个线程同时访问某些共享数据时,线程问题就会出现。正如其他人所提到的,从这里显示的代码来看,锁定是完全不必要的。