多线程访问非静态类的静态对象

时间:2011-06-03 09:29:26

标签: c# multithreading thread-safety concurrentdictionary

默认情况下,当通过多个线程访问时,非静态方法为每个线程都有自己的变量实例,因此如果它们不包含公共变量等,则会使它们成为线程安全的。

另一方面,静态方法中的变量在线程之间共享,默认情况下它们是非线程安全的。

说,我有一个班级,没有任何静态变量或方法。

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使用时是否是线程安全的?它们的变量是否会在线程之间共享,即使它们不是静态的,因为类的整个实例都是静态的?

3 个答案:

答案 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)

是的,这应该是线程安全设置。所有函数都将创建自己的“副本”函数局部变量。只有当您明确“触摸”共享属性时,您才会遇到问题。

然而,只有一个缓存,使得包含类静态将触及缓存而不是线程安全。