2

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

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

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

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

4

3 回答 3

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(回答 Chiao 的第二条评论):

ConcurrentDictionary<>有一个GetOrAdd()方法,它接受一个Func<TKey, TValue>不存在值的委托。

void AddToCacheIfItDoesntExist(int key)
{
    cache.GetOrAdd(key, SlowMethod);
}

int SlowMethod(int key)
{
    Thread.Sleep(1000);
    return key * 10;
}
于 2011-06-03T09:40:05.387 回答
1

在我看来,您似乎在断言静态方法的局部变量本身就是静态的。这不是真的。

局部变量对于实例和静态方法都是局部的,因此,不包括变量捕获等特殊情况,都存在于堆栈中。因此,它们对于方法的每个单独调用都是私有的。

于 2011-06-04T08:22:54.307 回答
0

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

但是,只有一个缓存,使包含的类成为静态将使接触缓存不是线程安全的。

于 2011-06-03T09:34:09.030 回答