【问题标题】:Multiple thread access to a static object of a non-static class多线程访问非静态类的静态对象
【发布时间】:2011-09-07 16:35:46
【问题描述】:

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

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

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

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

【问题讨论】:

  • 你必须有非常特殊的非静态方法,可以神奇地增强所有成员变量的线程安全:)
  • 公共静态 Profile objProfile = new Profile();不正确。
  • 实际上我已经使用这个初始化多年了。你能澄清一下你认为不正确的地方吗?

标签: c# multithreading thread-safety concurrentdictionary


【解决方案1】:

只要您只访问ConcurrentDictionary&lt;&gt; 实例cache,并且不要在Profile 方法之一中用新实例覆盖cache,它是线程安全的

因为是第二点,最好标记readonly

private readonly ConcurrentDictionary<int, int> cache = 
                     new ConcurrentDictionary<int, int>();

因为这表示您只能在 Profile 的实例化期间编写此成员。


编辑:

虽然ConcurrentDictionary&lt;&gt; 本身是线程安全的,但你仍然存在复合操作的非原子性问题。让我们看看两种可能的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&lt;&gt;.TryGetValue() 方法。


编辑 2(回答 Chiao 的第二条评论):

ConcurrentDictionary&lt;&gt; 有一个 GetOrAdd() method,它接受一个 Func&lt;TKey, TValue&gt; 代表不存在的值。

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

int SlowMethod(int key)
{
    Thread.Sleep(1000);
    return key * 10;
}

【讨论】:

  • 它不是打算被覆盖但你是对的,你不能太小心:) 谢谢。
  • 我总是尝试使用 Try*** 方法来实现原子性。但有一件事我无法解决。比如说,我必须将一个对象添加到并发字典中,但是这个对象需要一个与数据库的连接,以便一个复杂的 sp 花费大量资源来返回所需的值。所以我去: if (!cache.ContainsKey(key)) { cache.TryAdd(int, DBOperation);任何想法都将不胜感激,因为这是我对项目代码不满意的唯一事情......
  • 笨蛋,我从没想过 GetOrAdd() 会这样使用。 for(int i=0;i
  • 对于GetOrAdd() 是文档中的重要备注:If you call GetOrAdd simultaneously on different threads, addValueFactory may be called multiple times, but its key/value pair might not be added to the dictionary for every call.
【解决方案2】:

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

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

【讨论】:

    【解决方案3】:

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

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-09-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多