Почему в .NET встроен Hashtable для 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 Version = 4.0.0.0).
Возникает вопрос:
В чем причина наличия Thread.Sleep(1)
?
Ответы
Ответ 1
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 - это volatile bool. У автора были некоторые проблемы с английским "нарушение чтения" - это "неустойчивое чтение". Основная идея заключается в том, чтобы попытаться избежать уступки, переключатели контекста потока очень дороги, и некоторые надеются, что писатель быстро справится. Если это не отключается, то явно дается возможность избежать сжигания процессора. Сегодня это, вероятно, было написано Spinlock, но Hashtable очень старый. Как и предположения о модели памяти.
Ответ 2
Не имея доступа к остальной части кода реализации, я могу сделать только образованное предположение, основанное на том, что вы опубликовали.
Тем не менее, похоже, что он пытается что-то обновить в Hashtable, либо в памяти, либо на диске, и делает бесконечный цикл, ожидая его завершения (как видно из проверки isWriterInProgress
).
Если это одноядерный процессор, он может запускать только один поток за раз. Переход в непрерывный цикл, подобный этому, может легко означать, что у другого потока нет возможности запуска, но Thread.Sleep(1)
дает процессору шанс дать время писателю. Без ожидания нить писателя никогда не сможет запустить и никогда не завершится.
Ответ 3
Я не читал источник, но он выглядит как незаблокированная concurrency вещь. Вы пытаетесь читать из хеш-таблицы, но кто-то может писать на нее, поэтому вы ждете, пока isWriterInProgress
не будет отменен, и прочитанная версия не изменилась.
Это не объясняет, почему, например, мы всегда ждем хотя бы один раз. EDIT: это потому, что мы этого не делаем, спасибо @Maciej за указание на это. Когда нет споров, мы начинаем немедленно. Я не знаю, почему 8 - это магическое число, а не, например, 4 или 16.