Сделать синглтон-поток безопасным, используя частные статические методы? - PullRequest
1 голос
/ 30 июля 2011

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

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

Однако при вызове реальной статикиметод, новый фрейм стека создается, так что он в любом случае безопасен для потоков (сам по себе).Опять же, если я не ошибаюсь.

Будет ли этот шаблон для синглтона быть потокобезопасным?

class Singleton
{
    public object SomeMethod(object arg) {
        return Singleton.SomeMethodImpl(arg);

    }
    private static object SomeMethodImpl(object arg) {
        // is in unique stack frame?
        object Result;
        ...
        return Result;
    }
}

Если вам интересно, почему я не просто создаю статический класс вПервое место - мне нужно иметь синглтон, основанный на интерфейсе и имеющий различные реализации, как часть шаблона стратегии.Это не работает для статических классов.

Ответы [ 3 ]

5 голосов
/ 30 июля 2011

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

int AddTwo(int a, int b)
{
  return a + b;
}

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

Метод, такой как:

string ReverseString(string s)
{
    char[] charArray = s.ToCharArray();
    Array.Reverse( charArray );
    return new string( charArray );
}

Вышеуказанный метод также является реентерабельным и безопасным для потоков.

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

class BadExample
{
    private int counter;

    private void IncrementCounter()
    {
        ++counter;
    }
}

В приведенном выше примере метод IncrementCounter() не является потокобезопасным.

0 голосов
/ 30 июля 2011

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

Ваша главная забота о безопасности потоков? В этом случае безопасность потоков обычно применяется к экземпляру объекта, который является общим для потоков. Это означает, что до тех пор, пока вы не разделяете нормальный объект между потоками или не создаете статические данные на уровне класса, все должно быть в порядке.

Я добавляю пример использования синглтона с интерфейсами, с фабрикой и без нее. Примечание: я не запускал этот код.

public interface ISomething
{
    void Method();
}

public class Class1 : ISomething
{
    public void Method()
    {
        throw new NotImplementedException();
    }
}

public class Class2 : ISomething
{
    public void Method()
    {
        throw new NotImplementedException();
    }
}

public class Singleton
{
    private static ISomething privateObject;

    public static ISomething Instance()
    {
        lock (privateObject)
        {
            if (privateObject == null)
            {

                privateObject = new Class1();
            }
        }

        return privateObject;
    }
}

public class SingletonUsingFactory
{
    private static ISomething privateObject;

    public static ISomething Instance(int param)
    {
        lock (privateObject)
        {
            if (privateObject == null)
            {
                privateObject = FactoryClass.CreationObject(param);
            }
        }

        return privateObject;
    }
}

public static class FactoryClass
{
    public static ISomething CreationObject(int whatToCreate)
    {
        ISomething createdObject;

        switch (whatToCreate)
        {
            case 0:
                createdObject = new Class1();
                break;
            case 1:
                createdObject = new Class2();
                break;
            default:
                throw new Exception();
        }

        return createdObject;
    }
}
0 голосов
/ 30 июля 2011

Если я понимаю, что вы имеете в виду, тогда вы правы.

object Result;   // this is on its unique stack frame and is safe so far
Result = new ... // creating something on the heap that Result points to
                 // still safe because it's the only reference to it

Даже если несколько потоков вызовут это, они создадут разные новые переменные в куче и назначат их для Result в разных стеках.

Единственная опасность, в которой вы оказались бы, - это если бы у вас были личные поля.

Переменные внутри метода являются временными и видимыми только для этого метода call . Последующие или параллельные вызовы методов воссоздают эти переменные по отдельности.

Ваша единственная проблема - это статические поля или поля экземпляра. Они должны быть синхронизированы.

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