我一直在用静态类/方法编写很多代码,我相信它们会被多个线程同时调用/执行。所以我在我的方法中做了很多锁定。我通常会这样做:
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)我以为我需要与本地"副本"我在"锁定代码中的参数&#34 ;;因为如果另一个线程用param1和param2的不同值调用我的方法,那可能会搞砸我的处理。如果我只使用在锁定代码中声明/实例化的变量(即上例中的_param1和_param2),那么某些东西可能会改变param1和param2的值(或者发送对不同对象的引用),我没关系。但我需要这样做吗?我是不必要的偏执狂?
2)我决定在我需要之前不想实例化我的锁定对象,因为我的静态锁定对象集合正在增长......所以,我现在正在努力做到这一点:
private static Object staticLock1;
public static string StaticMethod1(string param1, int param2) {
lock(staticLock1 = staticLock1 ?? new Object()) {
(...)
}
}
有没有理由在第一次需要时实例化我的锁定对象是不安全的?在我的锁定语句中使用我的锁定对象上的赋值运算符会不会导致我出现问题或阻止对象被正确锁定?
答案 0 :(得分:2)
你所拥有的东西根本没有任何区别 - 在调用之后,参数本身不会改变,所以它什么也没做。在你的情况下使用字符串,看到字符串是不可变的是非常安全的。如果不是这种情况,则传递的任何内容都可能在其他地方被更改。在这种情况下,你必须制作一个真正的副本(即不只是复制参考),
考虑两个线程同时到达lock(staticLock1 = staticLock1 ?? new Object())
的情况。他们都可以看到staticLock1
为空。所以不,这不安全!
答案 1 :(得分:1)
您的主要困惑似乎是为了安全地同时安全地调用静态方法所需的同步。
数据争用总是会出现,因为多个线程以不同步的方式访问同一个存储位置,并且其中至少有一个是编写器。这个基本规则足以解释许多并发问题。
调用方法时,每个调用的参数都有不同的存储位置。他们是独立的。因此,调用相同方法的两个线程永远不会竞争访问方法参数。因此,您永远不需要同步对方法参数的访问。
当地人也是如此。实际上,参数具有与locals相同的同步属性。它们存在于每次通话中。
回答你的第二个问题:这是不安全的,因为两个线程可能会锁定不同的对象。此外,您在多个线程上写入staticLock1
未同步。我已经解释过这是一场数据竞赛。
答案 2 :(得分:0)
首先:创建Object实例的开销非常小。除非测量显示你应该这样做,否则不要担心。
只需像这样初始化:
private readonly static object staticLock1 = new Object();
您使用lock语句的方式是不安全。
其次:我在方法中看不到共享数据,所以没有理由锁定。
最后:如果设计包含许多静态函数,我会重新考虑它。