Является ли GetHashCode потокобезопасным? - PullRequest
2 голосов
/ 10 февраля 2010

У меня есть этот вопрос.

public class Foo : object
{
    public override bool Equals(obj a, objb)
    {
       return ((Foo)a).Bar.GetHashCode() == ((Foo)b).Bar.GetHashCode();
}

}

Предположим, я хочу сделать Foo безопасным. Нужно ли синхронизировать вызовы с GetHashCode ()?

Ответы [ 4 ]

2 голосов
/ 10 февраля 2010

Из документации на System.Object:

Открытые статические члены этого типа являются потокобезопасными. Члены экземпляра не гарантируют поточнобезопасность.

Таким образом, если вы не перегрузили Object.GetHashCode, у вас нет гарантии безопасности потока. Если вы перестарались, это зависит от вашей реализации. Это также зависит от того, что вы подразумеваете под " thread safe ."

2 голосов
/ 10 февраля 2010

Даже если бы вы собирались добавить синхронизацию, с чем бы вы синхронизировались?

Как правило, я бы сказал, GetHashCode должно всегда быть потокобезопасным, но в документах не указано требование по безопасности потоков. Потребитель Bar не может гарантировать, что его использование является поточно-ориентированным, если Bar не обеспечивает внутреннюю безопасность потоков.

Допустим, Bar использует два внутренних поля для вычисления хеш-кода. Если другой поток находится в процессе обновления полей и изменилось только одно, вы сможете получить результат GetHashCode() после обновления одного поля, но до обновления второго, если только Bar не обеспечивает внутреннюю синхронизацию.

0 голосов
/ 10 февраля 2010

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

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

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

Наконец, на самом деле не существует такой вещи, чтобы класс был универсально поточно-ориентированным. Вы можете синхронизировать каждый элемент экземпляра, но потребитель вашего класса может все еще испортить, если он не понимает семантику . Создание GetHashCode поточно-ориентированного подразумевает, что вы пытаетесь учесть каждый возможный сценарий многопоточности, но даже большинство поточно-безопасных классов являются поточно-ориентированными только для определенных операций , и GetHashCode обычно не один из них.

Я отсылаю вас к недавнему посту Эрика Липперта о безопасности потоков ; Подумайте о содержании этого при использовании фразы, такой как «Make Foo thread-safe.»

0 голосов
/ 10 февраля 2010

Это действительно зависит от того, что такое Бар. Если bar является строковым полем «только для чтения» в Foo, то оно является поточно-ориентированным. Если bar является полем для чтения и записи, содержащим произвольный тип значения со своим собственным определением GetHashCode (), тогда в вашей функции может не быть ничего поточно-ориентированного.

Это также зависит от вашего определения безопасности потока. Определяете ли вы безопасность потоков как означающее, что возвращаемое значение постоянно для всех? Если это так, то это невозможно гарантировать, если значение хэш-кода Бар не может измениться. Если нет, то вы должны признать, что возвращаемое значение этого метода может быть неправильным, учитывая текущие состояния a и b, прежде чем он даже вернется.

Если Бар может измениться, то это, возможно, неразрешимая проблема. Если вы попытаетесь заблокировать a и b (или заблокировать частные мьютексы в a и b), в каком порядке вы это делаете? Что если вызывающая сторона меняет порядок параметров? Это создаст очень простой тупик. Google концепция «иерархии замков», чтобы увидеть опасности в этом. Если Bar может измениться, единственный способ сделать эту операцию поточно-ориентированной - заблокировать доступ на запись ко всем барам, которые могут быть изменены с помощью одной глобальной блокировки перед выполнением сравнения.

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