Ориентированное на многопотоковое исполнение использование обновления DateTime Взаимно блокируется.*

/o модификатор находится в документация perlop вместо документация perlre , так как это - подобный кавычке модификатор, а не regex модификатор. Это всегда казалось нечетным мне, но это - то, как это. Начиная с Perl 5.20 это теперь перечислено в perlre просто, чтобы отметить, что Вы, вероятно, не должны использовать его.

Перед Perl 5.6, Perl перекомпилировал бы regex, даже если переменная не изменилась. Вы не должны больше делать этого. Вы могли использовать /o для компиляции regex однажды несмотря на дальнейшие изменения в переменной, но как другие отмеченные ответы, qr// лучше для этого.

12
задан camelCase 7 October 2009 в 13:31
поделиться

3 ответа

Да, вы можете это сделать. Ваша самая большая проблема может заключаться в том, что DateTime.Ticks имеет разрешение только ~ 20 мс. Так что на самом деле не имеет значения, сохраняете ли вы переменную DateTime last или long ticks . Но поскольку нет перегрузки Exchange для DateTime, вам нужно использовать long .

2
ответ дан 2 December 2019 в 22:05
поделиться

РЕДАКТИРОВАТЬ: на основе комментариев ниже от @romkyns [Спасибо]

Если ваш код работает на 32-битной машине. тогда 64-битная длина будет записана в память за две атомарные операции, которые могут быть прерваны переключением контекста. Таким образом, в общем случае вам необходимо решить эту проблему.

Но для ясности, для этого конкретного сценария (запись длинного значения, которое представляет временные тики) можно утверждать, что проблема настолько очень ] хотя и не имеет смысла иметь дело ... поскольку (за исключением доли секунды каждые 2 ^ 32 тика) значение в старшем слове (32 бита) в любом случае будет одинаковым для любых двух одновременных операций записи ... и даже в очень маловероятном случае, когда существуют две одновременные записи, которые охватывают эту границу, которые одновременно прерывают друг друга, и вы получаете верхнее слово из одного и нижнее слово из другого, если вы также не читаете это значение каждую миллисекунду, следующая запись все равно устранит проблему, и никакого вреда не будет. Однако, придерживаясь такого подхода, каким бы маловероятным ни казался плохой случай, по-прежнему допускает чрезвычайно тонкий, но возможный сценарий получения неправильного значения один раз за каждые 4 миллиарда тиков ... (И удачи в попытках воспроизвести эту ошибку ...)

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

0
ответ дан 2 December 2019 в 22:05
поделиться

Опция 1 : Используйте длину с блокировкой и dateTime.tobinary () . Это не нужно волатильный (на самом деле вы получите предупреждение, если у вас его было), потому что блокируется уже обеспечивает атомное обновление. Вы получаете точное значение DateTime таким образом.

long _lastHit;

void Touch()
{
    Interlocked.Exchange(ref _lastHit, DateTime.Now.ToBinary());
}

Чтобы прочитать это атомное значение:

DateTime GetLastHit()
{
    long lastHit = Interlocked.CompareExchange(ref _lastHit, 0, 0);
    return DateTime.FromBinary(lastHit);
}

Это возвращает значение _lasthit, и если бы это было 0 сворачивает его с 0 (то есть ничего не делает, чем прочитайте значение атомное значение).

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

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

DateTime _lastHit;
object _lock = new object();

void Touch()
{
    lock (_lock)
        _lastHit = DateTime.Now;
}

Вы должны Используйте блокировку, чтобы прочитать это значение тоже! Кстати, помимо взаимного исключения блокировка также гарантирует, что кэшированные значения не могут быть замечены, и чтение / пишеты не могут быть переупорядочены.

Нет варианта : ничего не делайте (просто напишите значение), отметили ли вы его как волатильный или нет. Это неправильно - даже если вы никогда не читаете значение, ваши пишеты на 32-битной машине могут перемещаться в такой несчастный способ, которым вы получаете поврежденную ценность:

Thread1: writes dword 1 of value 1
Thread2: writes dword 1 of value 2
Thread2: writes dword 2 of value 2
Thread1: writes dword 2 of value 1

Result: dword 1 is for value 2, while dword 2 is for value 1
9
ответ дан 2 December 2019 в 22:05
поделиться
Другие вопросы по тегам:

Похожие вопросы: