Являются ли эти частные статические члены потокобезопасными?

У меня есть следующий код с частными статическими членами.

Все эти классы говорят, что они потокобезопасны в библиотеке MSDN для «общедоступных статических» элементов.

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

 public static class passwordManager
{
    private static System.Security.Cryptography.SHA256 shaM = new System.Security.Cryptography.SHA256Managed();
    private static System.Security.Cryptography.RandomNumberGenerator rand = new System.Security.Cryptography.RNGCryptoServiceProvider();
    private static System.Text.Encoding enc = System.Text.Encoding.ASCII;

    public static string produceSalt(int size)
    {
        byte[] by = new byte[size];
        lock (rand)
        {
            rand.GetBytes(by);
        }
        return enc.GetString(by, 0, by.Length);
    }

    public static string encryptPassword(string password, string salt){

        return enc.GetString(shaM.ComputeHash(enc.GetBytes(password + salt)));
    }

    public static bool isCorrectPassword(string inputPassword, string DBsalt, string DBpassword)
    {
        return encryptPassword(inputPassword, DBsalt) == DBpassword;
    }

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

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

Спасибо,

Майк

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

    public static class passwordManager
{
    private static System.Security.Cryptography.RandomNumberGenerator rand = new System.Security.Cryptography.RNGCryptoServiceProvider();

    public static byte[] produceSalt(int size)
    {
        byte[] by = new byte[size];
        lock (rand)
        {
            rand.GetBytes(by);
        }

        return by;
    }

    public static byte[] encryptPassword(string password, byte[] salt){

        System.Security.Cryptography.SHA256 shaM = new System.Security.Cryptography.SHA256Managed();
        System.Text.Encoding enc = new System.Text.UTF8Encoding();

        return shaM.ComputeHash(concatArrays(enc.GetBytes(password), salt));
    }

    public static bool isCorrectPassword(string inputPassword, byte[] DBsalt, byte[] DBpassword)
    {
        return compare(encryptPassword(inputPassword, DBsalt), DBpassword);
    }
}

person Mike Saull    schedule 20.02.2012    source источник
comment
Извините, я неправильно понял терминологию библиотеки MSDN.   -  person Mike Saull    schedule 20.02.2012


Ответы (3)


Ваш код не является потокобезопасным.

Рассмотрим переменную System.Text.Encoding enc. Вы вызываете GetString, который является членом экземпляра. В документации говорится, что только общедоступные статические элементы являются потокобезопасными, поэтому, по заключению, GetString не является потокобезопасным, поскольку он не является общедоступным статическим членом.1

Этот код может не работать по следующим причинам:

  • Вы не пытались синхронизировать доступ к Encoding.GetString.
  • Encoding.GetString вызывается из общедоступного статического метода в вашем классе passwordManager.
  • Общедоступные статические методы с высокой вероятностью могут выполняться несколькими потоками одновременно.

Причина, по которой общедоступные статические методы почти всегда разрабатываются как потокобезопасные, заключается в том, что вызывающей стороне было бы неудобно всегда синхронизировать доступ к ним. Вы не можете ограничить многопоточный доступ к статическим членам, как вы можете это сделать с членами экземпляра. Рассмотрим, например, приложение ASP.NET. Запросы веб-страниц часто обрабатываются одновременно в отдельных потоках. Вы хотите использовать lock каждый раз при вызове статического метода? Конечно, нет. Это нелепое бремя для разработчика.

Обновление:

Ваш новый код теперь потокобезопасен. Вам нужно будет провести несколько эталонных тестов, чтобы увидеть, какой способ быстрее: использование lock или создание новых экземпляров при каждом вызове, как сейчас. Я не удивлюсь, если lock будет быстрее.


1То же самое можно сказать и о shaM.ComputeHash и enc.GetBytes.

person Brian Gideon    schedule 20.02.2012
comment
Да, я считаю, что это было бы намного быстрее, однако в ситуации экстремальной нагрузки блокировка одного статического объекта может быть намного медленнее, чем просто создание нового объекта, если для них достаточно ресурсов сервера. Мой вариант использования находится в ситуации с очень низкой нагрузкой, поэтому в любом случае он будет достаточно быстрым и отзывчивым. - person Mike Saull; 27.02.2012

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

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

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

person Fakrudeen    schedule 20.02.2012

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

person Trevor Pilley    schedule 20.02.2012