当并行调用方法时,我需要同步访问方法参数和局部变量吗?
本文关键字:方法 参数 访问 局部变量 同步 调用 并行 | 更新日期: 2023-09-27 18:13:59
我一直在用静态类/方法编写大量代码,我相信这些代码将被多个线程同时调用/执行。我在方法中做了很多锁。我通常这样做:
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(staticLock1 = staticLock1 ?? new Object())
的情况。它们都认为staticLock1
为零。所以不,那不安全!
您的主要困惑似乎是需要什么同步才能安全地并发调用静态方法。
数据竞争总是会出现,因为多个线程以不同步的方式访问同一个存储位置,并且其中至少有一个是写线程。这个基本规则足以解释很多并发性问题。当你调用一个方法时,每次调用的参数都有不同的存储位置。它们是独立的。因此,调用同一方法的两个线程永远不会竞相访问方法参数。因此,您永远不需要同步访问方法参数。
对于locals也是一样。事实上,参数与局部变量具有相同的同步属性。
回答第二个问题:这是不安全的,因为两个线程可能锁定不同的对象。此外,您在多个线程上不同步地写入staticLock1
。我已经解释过这是一场数据竞赛。
首先:创建Object实例的开销非常小。不用担心,除非测量结果表明你应该这样做。
就像这样初始化:
private readonly static object staticLock1 = new Object();
你使用锁语句的方式是不安全的
第二:我在方法中没有看到共享数据,所以没有理由锁定。
最后:如果它包含许多静态函数,我会重新考虑设计。