Как сделать потокобезопасный Dictionary
От: Аноним  
Дата: 19.10.10 18:03
Оценка:
Есть метод DoSomething в котором происходит асинхронный запрос к БД для работы с объектом имеющего уникальный ID, нужно чтобы пока обрабатывается объект новый асинхронный запрос не прошел.


public static void DoSomething( int ID )
{
   /// Здесь асинхронный вызов некой хранимки с передачей ей ID,
}

public static void DoSomethingFinished( .. )
{
  /// Здесь получаем результаты асинхронного запроса
}



т.е. нужно немного доработать , примерно так


private static Dictionary<int, bool> m_InProc;

public static void DoSomething( int ID )
{
   if ( m_InProc.ContainKey( ID ) )
   {
      if ( m_InProc[ID] )
         return;
   }

   m_InProc[ID] = true;
   /// Здесь асинхронный вызов некой хранимки с передачей ей ID,
}

public static void DoSomethingFinished( .. )
{
  /// Здесь получаем результаты асинхронного запроса
  m_InProc[ID] = false;
}


Возможно для этих целей лучше использовать какие-то объекты синхронизации ( мутекс и т.п. ).
Re: Как сделать потокобезопасный Dictionary
От: Аноним  
Дата: 19.10.10 18:32
Оценка: +1
Здравствуйте, Аноним, Вы писали:

А>Есть метод DoSomething в котором происходит асинхронный запрос к БД для работы с объектом имеющего уникальный ID, нужно чтобы пока обрабатывается объект новый асинхронный запрос не прошел.


А>[c#]


Как то странно получается: "Синхронный запрос через асинхронные методы" В чём фишка?
Re[2]: Как сделать потокобезопасный Dictionary
От: Аноним  
Дата: 19.10.10 18:40
Оценка:
Здравствуйте, Аноним, Вы писали:

А>Здравствуйте, Аноним, Вы писали:


А>>Есть метод DoSomething в котором происходит асинхронный запрос к БД для работы с объектом имеющего уникальный ID, нужно чтобы пока обрабатывается объект новый асинхронный запрос не прошел.


А>>[c#]


А>Как то странно получается: "Синхронный запрос через асинхронные методы" В чём фишка?


Нет это не синхронный запрос, если заметили то там просто return.
То есть если 2 потока пихнут на обработку одинаковый ID, например =1 , то пока первый обрабатывается второй будет игнорироваться, процедура не "зависает" как при синхронном запросе , а просто игнорирует дублирующий запрос.
При этом 2й поток может послать DoSomething(2) и DoSomething(3) которые будут работать паралельно с DoSomething(1)

Или один поток может вызвать DoSomething(1) и через некоторое время послать DoSomething(2), DoSomething(3) и еще раз DoSomething(1) , а первый запрос еще может не завершится.
Re[3]: Как сделать потокобезопасный Dictionary
От: victor_kr Украина  
Дата: 19.10.10 21:08
Оценка:
Я не профессионал многопоточности, но возможно, можно сделать так.

Создать объект для блокировки:

private static object dictionaryLock = new object();

Обернуть все обращения к dictionary конструкцией lock (dictionaryLock) {...}.

Таким образом вы сделаете работу с dictionary потокобезопасной. Остальная часть кода (длительно выполняющаяся) будет запускаться однократно для каждого ID только после потокобезопасной проверки словаря.
Re[4]: Как сделать потокобезопасный Dictionary
От: Аноним  
Дата: 20.10.10 04:01
Оценка:
Здравствуйте, victor_kr, Вы писали:

_>Я не профессионал многопоточности, но возможно, можно сделать так.


_>Создать объект для блокировки:


_>private static object dictionaryLock = new object();


_>Обернуть все обращения к dictionary конструкцией lock (dictionaryLock) {...}.


_>Таким образом вы сделаете работу с dictionary потокобезопасной. Остальная часть кода (длительно выполняющаяся) будет запускаться однократно для каждого ID только после потокобезопасной проверки словаря.


Интересно, а у object вроде бы нет свойств типа "сигнальное состояние". Соотвественно lock( myobject ) наверное управляет состоянием посредством ссылки на object , а сам object при этом не затрагивается . Почему бы в таком случае не лочить сразу статический объект Dictionary , т.е.

lock( myDictionary )
{
  /// работа с  myDictionary.
}
Re[5]: Как сделать потокобезопасный Dictionary
От: microcod США www.tehnoromantik.net
Дата: 20.10.10 05:37
Оценка:
Здравствуйте, Аноним, Вы писали:

А>Здравствуйте, victor_kr, Вы писали:


_>>Я не профессионал многопоточности, но возможно, можно сделать так.


_>>Создать объект для блокировки:


_>>private static object dictionaryLock = new object();


_>>Обернуть все обращения к dictionary конструкцией lock (dictionaryLock) {...}.


_>>Таким образом вы сделаете работу с dictionary потокобезопасной. Остальная часть кода (длительно выполняющаяся) будет запускаться однократно для каждого ID только после потокобезопасной проверки словаря.


А>Интересно, а у object вроде бы нет свойств типа "сигнальное состояние". Соотвественно lock( myobject ) наверное управляет состоянием посредством ссылки на object , а сам object при этом не затрагивается . Почему бы в таком случае не лочить сразу статический объект Dictionary , т.е.

А>

А>lock( myDictionary )
А>{
А>  /// работа с  myDictionary.
А>}

А>


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

lock(var_1){
lock(var_2){
    //здесь потокобезопасный 
    //доступ к var_1 и var_2
  }
}

Программа — мысли спрессованные в код.
Re[5]: Как сделать потокобезопасный Dictionary
От: GlebZ Россия  
Дата: 20.10.10 06:54
Оценка:
Здравствуйте, Аноним, Вы писали:

Вероятнее всего можно, но так не принято. Объект может локироваться самим объектом, либо другим чужим или полузабытым кодом. Что может привести к дедлокам. Поэтому принято локировать явно через свои object.
Re: Как сделать потокобезопасный Dictionary
От: TK Лес кывт.рф
Дата: 20.10.10 07:37
Оценка: +1
Здравствуйте, Аноним, Вы писали:

А>Возможно для этих целей лучше использовать какие-то объекты синхронизации ( мутекс и т.п. ).


Для этих целей можно взять ReaderWriterLock. Dictionary потокобезопасна для чтения.

A Dictionary(Of TKey, TValue) can support multiple readers concurrently, as long as the collection is not modified. Even so, enumerating through a collection is intrinsically not a thread-safe procedure. In the rare case where an enumeration contends with write accesses, the collection must be locked during the entire enumeration. To allow the collection to be accessed by multiple threads for reading and writing, you must implement your own synchronization.

Если у Вас нет паранойи, то это еще не значит, что они за Вами не следят.
Re: Как сделать потокобезопасный Dictionary
От: Lloyd Россия  
Дата: 20.10.10 07:59
Оценка:
Здравствуйте, Аноним, Вы писали:

А>Возможно для этих целей лучше использовать какие-то объекты синхронизации ( мутекс и т.п. ).


Посмотрите ReaderWriterLockSlim. Там в примерах как раз рассмотрен ваш случай.
Re[5]: Как сделать потокобезопасный Dictionary
От: _FRED_ Черногория
Дата: 20.10.10 08:19
Оценка:
Здравствуйте, Аноним, Вы писали:

А>Интересно, а у object вроде бы нет свойств типа "сигнальное состояние". Соотвественно lock( myobject ) наверное управляет состоянием посредством ссылки на object , а сам object при этом не затрагивается . Почему бы в таком случае не лочить сразу статический объект Dictionary


Потому что очень бывает полезно читать, хотя бы документацию:

In general, avoid locking on a public type, or instances beyond your code's control.

Best practice is to define a private object to lock on, or a private static object variable to protect data common to all instances.

Help will always be given at Hogwarts to those who ask for it.
Re[2]: Как сделать потокобезопасный Dictionary
От: Lexey Россия  
Дата: 20.10.10 15:04
Оценка:
Здравствуйте, Lloyd, Вы писали:

L>Посмотрите ReaderWriterLockSlim. Там в примерах как раз рассмотрен ваш случай.


У Joe Duffy в блогах было неплохое сравнение реализаций разных ReaderWriter'ов, критической секции и спинлоков. Вывод довольно забавный: readerwriter'ы в случае частой записи прилично сливают обычным критическим секциям и спинлокам.
"Будь достоин победы" (c) 8th Wizard's rule.
Re[6]: Как сделать потокобезопасный Dictionary
От: koandrew Канада http://thingselectronic.blogspot.ca/
Дата: 20.10.10 20:02
Оценка: +1
Здравствуйте, GlebZ, Вы писали:

GZ>Вероятнее всего можно, но так не принято. Объект может локироваться самим объектом, либо другим чужим или полузабытым кодом. Что может привести к дедлокам. Поэтому принято локировать явно через свои object.


Это кем так принятно? Если мне память не изменяет, в гайдлайнах наоборот написано, чтобы сами классы не лочились на this — именно потому, что на них может лочиться внешний код. Вы не предлагаете просто плодить сущности (помимо комплекта полей-данных иметь параллельный комплект полей-"локеров" и не забывать всё это поддерживать тоже параллельно)...
[КУ] оккупировала армия.
Re[6]: Как сделать потокобезопасный Dictionary
От: koandrew Канада http://thingselectronic.blogspot.ca/
Дата: 20.10.10 20:05
Оценка: +1
Здравствуйте, _FRED_, Вы писали:

_FR>

_FR>In general, avoid locking on a public type, or instances beyond your code's control.
_FR>…
_FR>Best practice is to define a private object to lock on, or a private static object variable to protect data common to all instances.


Насколько я понял, это относится к дизайну библиотек (потому что у "конечного" кода не может быть "instances beyond your code's control"). Соответственно не рекомендуют лочиться в библиотеках именно потому, что вызывающий код может (и будет) лочиться на них. Не зря же упомянули только публичные типы
[КУ] оккупировала армия.
Re[6]: Как сделать потокобезопасный Dictionary
От: koandrew Канада http://thingselectronic.blogspot.ca/
Дата: 20.10.10 20:09
Оценка: +1
Здравствуйте, _FRED_, Вы писали:

Кстати в одной ссылке от вашей страницы как раз и написано (http://msdn.microsoft.com/en-us/library/ms173179.aspx ):

Strictly speaking, the object provided is used solely to uniquely identify the resource being shared among multiple threads, so it can be an arbitrary class instance. In practice, however, this object usually represents the resource for which thread synchronization is necessary. For example, if a container object is to be used by multiple threads, then the container can be passed to lock, and the synchronized code block following the lock would access the container. As long as other threads locks on the same contain before accessing it, then access to the object is safely synchronized.

[КУ] оккупировала армия.
Re: Как сделать потокобезопасный Dictionary
От: Jolly Roger  
Дата: 21.10.10 02:53
Оценка:
Здравствуйте, Аноним, Вы писали:

А сам словарь требует защиты? То есть возможно-ли добавление-удаление в него элементов из разных потоков, или, допустим одновременно с вызовом DoSomething или DoSomethingFinished? Если нет, синхронизировать доступ к словарю нет смысла.

Если есть возможность расширить хранящиеся объекты, то можно добавить им методы Acquire-Release. Если возможности нет, можно создать контейнер для хранения их в словаре, типа
private class DictItem<T>
{
    private int _locked;
    public bool Acquire(){return 0 == Interlocked.Exchenge(ref _locked, 1);}
    public void Release(){Interlocked.Exchenge(ref _locked, 0);}
    public T Item;
}

Можно конечно хранить состояние в отдельном массиве, но лично мне это не нравится. Признаться, мне эта ситуация вообще не нравится, боюсь, в будущем она может создать немало проблем, но Вам на месте должно быть виднее.
"Нормальные герои всегда идут в обход!"
Re[7]: Как сделать потокобезопасный Dictionary
От: _FRED_ Черногория
Дата: 21.10.10 03:54
Оценка:
Здравствуйте, koandrew, Вы писали:

_FR>>In general, avoid locking on a public type, or instances beyond your code's control.
_FR>>…
_FR>>Best practice is to define a private object to lock on, or a private static object variable to protect data common to all instances.


K>Насколько я понял, это относится к дизайну библиотек (потому что у "конечного" кода не может быть "instances beyond your code's control").


Я почему-то не знаю, что такое "конечный код" и чем он отличается от какого-то другого кода.

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


Почему слово public обязательно следует понимать буквально? Ничто не мешает понимать его как видимое где-то снаружи.
Help will always be given at Hogwarts to those who ask for it.
Re[7]: Как сделать потокобезопасный Dictionary
От: _FRED_ Черногория
Дата: 21.10.10 04:08
Оценка: +1
Здравствуйте, koandrew, Вы писали:

K>Кстати в одной ссылке от вашей страницы как раз и написано (http://msdn.microsoft.com/en-us/library/ms173179.aspx ):

K>

Strictly speaking, the object provided is used solely to uniquely identify the resource being shared among multiple threads, so it can be an arbitrary class instance. In practice, however, this object usually represents the resource for which thread synchronization is necessary. For example, if a container object is to be used by multiple threads, then the container can be passed to lock, and the synchronized code block following the lock would access the container. As long as other threads locks on the same contain before accessing it, then access to the object is safely synchronized.


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

В МСДН есть не мало очень умных мыслей. А есть и просто вредные.

В примере в статье, на которую вы ссылаетесь (как, кажется, и везде в МСДН), lockThis даже без ридлонли объявлен — что конечно же никак не влият на _работоспособность_ кода, но может оказаться весьма важным при использовании/поддержке/рефакторинге/переписывании.

Например, в данной статье никак не сказано, как процитированный кусок сочитается с SRP, однако весьма разумно держать это у себя в голове постоянно, и экземпляры объектов (" container object") использовать для одних целей (для которых они напрямую предназначены), а для синхронизации использовать нечто другое, самостоятельное.
Help will always be given at Hogwarts to those who ask for it.
Re[7]: Как сделать потокобезопасный Dictionary
От: Константин Л. Франция  
Дата: 21.10.10 11:46
Оценка:
Здравствуйте, koandrew, Вы писали:

K>Здравствуйте, GlebZ, Вы писали:


GZ>>Вероятнее всего можно, но так не принято. Объект может локироваться самим объектом, либо другим чужим или полузабытым кодом. Что может привести к дедлокам. Поэтому принято локировать явно через свои object.


K>Это кем так принятно? Если мне память не изменяет, в гайдлайнах наоборот написано, чтобы сами классы не лочились на this — именно потому, что на них может лочиться внешний код. Вы не предлагаете просто плодить сущности (помимо комплекта полей-данных иметь параллельный комплект полей-"локеров" и не забывать всё это поддерживать тоже параллельно)...


1. многими
2. это не "плодить сущности", это писать безопасный код
Re[7]: Как сделать потокобезопасный Dictionary
От: Константин Л. Франция  
Дата: 21.10.10 11:47
Оценка:
Здравствуйте, koandrew, Вы писали:

K>Здравствуйте, _FRED_, Вы писали:


_FR>>

_FR>>In general, avoid locking on a public type, or instances beyond your code's control.
_FR>>…
_FR>>Best practice is to define a private object to lock on, or a private static object variable to protect data common to all instances.


K>Насколько я понял, это относится к дизайну библиотек (потому что у "конечного" кода не может быть "instances beyond your code's control"). Соответственно не рекомендуют лочиться в библиотеках именно потому, что вызывающий код может (и будет) лочиться на них. Не зря же упомянули только публичные типы


это нормальное требование для любого кода.
Re[8]: Как сделать потокобезопасный Dictionary
От: victor_kr Украина  
Дата: 21.10.10 11:51
Оценка:
Объекты бизнес-логики не стоит использовать для блокировок, так как у них есть свое назначение. Эти объекты могут удаляться/изменяться/пересоздаваться. Объект блокировки создается для того чтобы решать одну задачу — блокировка доступа. Таким образом можно разделить ответственность и избежать проблем. При использовании отдельных блокирующих объектов не нужно помнить о том, что нельзя пересоздать объект бизнес-логики потому что он где-то используется в качестве блокировки.
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.