Можно ли использовать строку в качестве объекта блокировки?

Мне нужно сделать критическую секцию в области на основе конечного набора строк. Я хочу, чтобы блокировка была разделена для одного и того же экземпляра строки (несколько похоже наString.Intern подход).

Я рассматриваю следующую реализацию:

public class Foo
{
    private readonly string _s;
    private static readonly HashSet _locks = new HashSet();

    public Foo(string s)
    {
        _s = s;
        _locks.Add(s);
    }

    public void LockMethod()
    {
        lock(_locks.Single(l => l == _s))
        {
            ...
        }
    }
}

Есть ли проблемы с этим подходом? Можно ли таким образом блокировать строковый объект, и есть ли какие-либо проблемы безопасности потоков при использовании?HashSet

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

Окончательная реализация

На основе предложений я пошел со следующей реализацией:

public class Foo
{
    private readonly string _s;
    private static readonly ConcurrentDictionary _locks = new ConcurrentDictionary();

    public Foo(string s)
    {
        _s = s;
    }

    public void LockMethod()
    {
        lock(_locks.GetOrAdd(_s, _ => new object()))
        {
            ...
        }
    }
}
 Oliver09 дек. 2014 г., 00:11
Что касается проблем с неравными строковыми экземплярами, упомянутымиJonSkeet а такжеBrianGideonЯбуду предлагать использоватьlock(_locks.GetOrAdd(String.Intern(_s), s => new object())), хотя интернирование может быть все еще отключено черезCompilationRelaxations.NoStringInterning а потом это победилотоже не работает. Может быть, используя строкуhashcode как ключ в словаре поможет тут ?!
 Brian Gideon11 окт. 2012 г., 22:28
Я отмечаю некоторые из особенностей использованияstring как цель блокировки в моем ответеВот.
 Zaid Masud17 февр. 2019 г., 00:48
Интернация @Oliver является проблемой, когда вы блокируете строку (кто-то другой может заблокировать тот же экземпляр интернированной строки). Здесь мы фиксируем новый экземпляр объекта, а не экземпляр строки. Никто другой не имеет доступа к этому новому экземпляру объекта.
 Neil04 нояб. 2014 г., 18:55
Могу ли я предложить небольшое изменение? lock (_locks.GetOrAdd (_s, s => new object ())) Это делает так, что вы создаете только один объект на строку, а не создаете его каждый раз, когда вызываете LockMethod ().

Ответы на вопрос(3)

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

Строковые строки для каждого процесса, поэтому они даже используются разными доменами приложений. То же самое касается объектов типа (так что неT заблокировать на typeof (x)) либо.

 user100546223 нояб. 2016 г., 17:51
интерес, это где-то описано или кто-то проверял?
 Brian Rasmussen11 окт. 2012 г., 15:26
Литеральные строки интернированы по умолчанию, но вы можете интернировать любую строку, если хотите. Дело в том, что вы можетеСкажите, является ли строка интернированной или нет, просто взглянув на использование ссылки на строку. ИМО тамНет смысла блокировать строку и, учитывая, что в некоторых ситуациях это может привести к трудностям при поиске, лучше просто избегать этого.
 Brian Rasmussen23 нояб. 2016 г., 21:38
@ user1005462 это легко проверить в отладчике, так как один и тот же экземпляр будет возвращен через домены приложения.
 Zaid Masud11 окт. 2012 г., 14:56
Кстати, кажется, что интернирование строктолько проблема для буквенных строк, Так что если строки не создаются как литералы, то, на мой взгляд, все это немного преувеличено.
 Zaid Masud11 окт. 2012 г., 23:14
@BrianRasmussen да, согласился.
 Brian Rasmussen09 окт. 2012 г., 19:32
@leppie, конечно, но может быть неочевидно, что строки / объекты типа имеют это "особенность".
 leppie09 окт. 2012 г., 19:14
Иногда переменные блокировки для процесса - это то, что вы ищете; p
Решение Вопроса

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

Теперь это, вероятно, надуманный сценарий в большинстве конкретных ситуаций. Это'Более общее правило для библиотек.

Но с другой стороны, в чем заключаются преимущества струн?

Итак, точка за точкой:

Есть ли проблемы с этим подходом?

Да, но в основном теоретический.

Можно ли таким образом блокировать строковый объект, и есть ли какие-либо проблемы безопасности потоков при использовании HashSet?

HashSet не участвует в безопасности потока, пока потоки только читают одновременно.

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

Да. Просто чтобы быть на безопасной стороне. В большой системе главная цель избежать взаимоблокировки - сохранить объекты блокировки как можно более локальными и частными. Только ограниченный объем кода должен иметь доступ к ним.

 Zaid Masud09 окт. 2012 г., 19:30
Принято, как мне показалось, этот ответ наиболее полно касался конкретных вопросов.

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

(Лично мне не нравится тот факт, что каждый объект имеет монитор в первую очередь, но этонемного другое беспокойство.)

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

public sealed class Lock
{
    private readonly string name;

    public string Name { get { return name; } }

    public Lock(string name)
    {
        if (name == null)
        {
            throw new ArgumentNullException("name");
        }
        this.name = name;
    }
}

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

 Jon Skeet17 авг. 2015 г., 22:15
@ Алекс: Нет, я действительно неЯ так не думаю - тыв основном у меня есть глобальное пространство имен для блокировки ... откуда вы знаете, какой другой код будет использоваться? Да, интернирование гарантировало бы, что вы получите блокировку для этого имени, но гдеСтрогость в том, чтобы использовать только соответствующий код?
 Jon Skeet09 окт. 2012 г., 19:31
@ZaidMasud: Здесь есть две совершенно разные проблемы: а) глобальная коллекция блокировок (по возможности избегайте); б) что заблокировать (не строки; объект будет в порядке, замок будет лучше)
 Zaid Masud09 окт. 2012 г., 19:07
Спасибо ... будет статичнымConcurrentDictionary заменитьHashSet что создает новый объект блокировки для каждой строки, хороший подход?
 Jon Skeet09 окт. 2012 г., 19:12
@HenkHolterman: я очень хотел создать определенный тип, например класс называетсяЗамок".
 Alex18 авг. 2015 г., 11:29
@ Джон, спасибо за ответ, это имеет смысл
 Alex17 авг. 2015 г., 18:38
Wouldn»т 1) использование явногоthis.name = String.Intern(name); и 2) использование, например, константный префикс для имениthis.name = String.Intern(Prefix + name); быть улучшением дляLock учебный класс?
 Jon Skeet09 окт. 2012 г., 19:17
@ZaidMasud: Готово. Это действительно очень просто ...
 Jon Skeet09 окт. 2012 г., 19:09
@ZaidMasud: Вывсе еще иметь статический (глобальный) набор блокировок, который неЭто звучит как хороший дизайн для меня. Я очень редко нахожу, что статика для изменчивого состояния оказывается хорошей вещью.
 Zaid Masud09 окт. 2012 г., 19:12
я не уверен, если яЯ следую за этим полностью ... если этоВ частном статическом словаре объекты блокировки будут содержаться только для этого класса. Также я'Я не уверен, как будет выглядеть новый тип блокировки, если только вы не имеете в виду новый экземпляр объекта, как предлагает @HenkHolterman.
 Zaid Masud09 окт. 2012 г., 19:29
+1 за подход и идею ... хотя если ям чего-то не хватает в случаяхLock объекты все равно должны были бы где-то храниться статически, так что, насколько я понимаю, преимущество нового типа в значительной степени заключается в удобочитаемости.
 Zaid Masud09 окт. 2012 г., 19:15
@JonSkeet Я бы предложил пример кода из предложенногоLock введите ответ.
 AndrewE15 янв. 2019 г., 22:49
Спасибо @JonSkeet У меня возникла проблема, когда я использовал несколько потоков, у нас был доступ к одной и той же строке, но с разными значениями в каждом потоке, в моем случае это строка подключения, короче говоря, иногда строка была бы значением другой строки подключения, даже если хотя это не было установлено, использование вышеуказанного замка, как класс работал! благодарю вас!
 Jon Skeet09 окт. 2012 г., 19:13
@ZaidMasud: этовсе еще статичен, что делает боль в испытаниях и т. д.глобальная переменная, даже если онане глобальноvisbile...Lock класс не будетне нужно иметь каких-либо дополнительных членов, если вы не хотитеname свойство (и переопределитьToString) - суть в том, что всем, читающим код, будет предельно ясно, что такое переменная типаLock был для.

Ваш ответ на вопрос