默认情况下,当通过多个线程访问时,非静态方法为每个线程都有自己的变量实例,因此如果它们不包含公共变量等,则会使它们成为线程安全的。
另一方面,静态方法中的变量在线程之间共享,默认情况下它们是非线程安全的。
说,我有一个班级,没有任何静态变量或方法。
public class Profile {
private ConcurrentDictionary<int, int> cache =
new ConcurrentDictionary<int, int>();
public AddToCache() {
}
public RemoveToCache() {
}
public DoSomethingThatShouldBeThreadSafe() {
}
}
但后来我从这个类中创建了一个静态对象。
public static Profile objProfile = new Profile();
然后,使用多个线程访问objProfile。
问题是,Profile类,AddToCache,RemoveFromCache和DoSomethingThatShouldBeThreadSafe的方法是否在通过objProfile使用时是否是线程安全的?它们的变量是否会在线程之间共享,即使它们不是静态的,因为类的整个实例都是静态的?
答案 0 :(得分:8)
只要您只访问ConcurrentDictionary<>
实例cache
,并且不使用cache
之一的新实例覆盖Profile
- 方法是线程安全的。
由于第二点,最好将其标记为readonly
,
private readonly ConcurrentDictionary<int, int> cache =
new ConcurrentDictionary<int, int>();
因为这表示您只能在实例化Profile
时编写此成员。
<小时/> 修改强>
尽管ConcurrentDictionary<>
本身是线程安全的,但仍然存在复合操作的非原子性问题。我们来看看两种可能的GetFromCache()
方法。
int? GetFromCacheNonAtomic(int key)
{
if (cache.ContainsKey(key)) // first access to cache
return cache[key]; // second access to cache
return null;
}
int? GetFromCacheAtomic(int key)
{
int value;
if (cache.TryGetValue(key, out value)) // single access to cache
return value;
return null;
}
只有第二个是原子的,因为它使用ConcurrentDictionary<>.TryGetValue()
方法。
<小时/> 编辑2(回答乔的第2条评论):
ConcurrentDictionary<>
包含GetOrAdd()
method,其中Func<TKey, TValue>
代表获取不存在的值。
void AddToCacheIfItDoesntExist(int key)
{
cache.GetOrAdd(key, SlowMethod);
}
int SlowMethod(int key)
{
Thread.Sleep(1000);
return key * 10;
}
答案 1 :(得分:1)
在我看来,你断言静态方法的局部变量本身就是静态的。事实并非如此。
本地变量对于实例和静态方法总是本地的,因此,除了变量捕获等特殊情况之外,它们存在于堆栈中。因此,它们对方法的每个单独调用都是私有的。
答案 2 :(得分:0)
是的,这应该是线程安全设置。所有函数都将创建自己的“副本”函数局部变量。只有当您明确“触摸”共享属性时,您才会遇到问题。
然而,只有一个缓存,使得包含类静态将触及缓存而不是线程安全。