Почему во внутреннем Hashtable.NET есть Thread.Sleep(1)?

Недавно я читал реализацию.NET Hashtable и столкнулся с фрагментом кода, который я не понимаю. Часть кода:

int num3 = 0;
int num4;
do
{
   num4 = this.version;
   bucket = bucketArray[index];
   if (++num3 % 8 == 0)
     Thread.Sleep(1);
}
while (this.isWriterInProgress || num4 != this.version);

Весь код в пределах public virtual object this[object key] из System.Collections.Hashtable (Версия mscorlib =4.0.0.0).

Вопрос в том:

В чем причина наличия Thread.Sleep(1) там?

3 ответа

Решение

Sleep(1) - это документированный способ в Windows дать процессор и запустить другие потоки. Вы можете найти этот код в справочном источнике с комментариями:

   // Our memory model guarantee if we pick up the change in bucket from another processor,
   // we will see the 'isWriterProgress' flag to be true or 'version' is changed in the reader.
   //
   int spinCount = 0;
   do {
       // this is violate read, following memory accesses can not be moved ahead of it.
       currentversion = version;
       b = lbuckets[bucketNumber];

       // The contention between reader and writer shouldn't happen frequently.
       // But just in case this will burn CPU, yield the control of CPU if we spinned a few times.
       // 8 is just a random number I pick.
       if( (++spinCount) % 8 == 0 ) {
           Thread.Sleep(1);   // 1 means we are yeilding control to all threads, including low-priority ones.
       }
   } while ( isWriterInProgress || (currentversion != version) );

Переменная isWriterInProgress является изменяемым логическим значением. У автора возникли проблемы с английским "нарушать чтение" - "изменчивое чтение". Основная идея состоит в том, чтобы попытаться избежать уступок, переключение контекста потока очень дорого, с некоторой надеждой, что автор выполнит это быстро. Если это не сработает, то явно уступите, чтобы избежать сжигания процессора. Это, вероятно, было бы написано сегодня со Spinlock, но Hashtable очень старый. Как и предположения о модели памяти.

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

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

Если это одноядерный процессор, он может запускать только один поток за раз. Подобный непрерывный цикл может легко означать, что другой поток не имеет шансов на запуск, но Thread.Sleep(1) дает процессору возможность дать время писателю. Без ожидания поток записи может никогда не получить возможность запуска и никогда не завершится.

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

Это не объясняет, почему, например, мы всегда ждем хотя бы один раз. РЕДАКТИРОВАТЬ: это потому, что мы не, спасибо @Maciej за указание на это. Когда нет споров, мы немедленно приступаем. Я не знаю, почему 8 магическое число вместо, например, 4 или 16, хотя.

Другие вопросы по тегам