Многопоточный доступ к статическому объекту нестатического класса - PullRequest
2 голосов
/ 03 июня 2011

По умолчанию нестатические методы имеют свой собственный экземпляр переменных для каждого потока при доступе через несколько потоков, что делает их потокобезопасными, если они не содержат общедоступную переменную и т. Д.

С другой стороны,переменные в статических методах распределяются между потоками, что делает их не поточно-безопасными по умолчанию.

Скажем, у меня есть класс, не имеющий статических переменных или методов вообще.

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 ]

8 голосов
/ 03 июня 2011

Пока вы обращаетесь только к экземпляру 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().


EDIT2 (ответ на 2-й комментарий Чао):

ConcurrentDictionary<> имеет метод GetOrAdd() , который принимает делегат Func<TKey, TValue> для несуществующих значений.

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

int SlowMethod(int key)
{
    Thread.Sleep(1000);
    return key * 10;
}
1 голос
/ 04 июня 2011

Мне кажется, вы утверждаете, что локальные переменные статического метода сами по себе являются статическими. Это не правда.

Локальные переменные всегда локальны как для экземпляра, так и для статических методов, поэтому, за исключением особых случаев, таких как захват переменных, они находятся в стеке. Таким образом, они являются частными для каждого отдельного вызова метода.

0 голосов
/ 03 июня 2011

Да, это должна быть поточно-ориентированная установка. Все функции будут создавать свои собственные «копии» локальных переменных функции. Только когда вы явно «дотронетесь» до общих свойств, у вас возникнут проблемы.

Однако будет только ОДИН кеш, а статический содержащий класс сделает касание кеша НЕ безопасным для потока.

...