ManualResetEvent - как здесь может возникнуть состояние гонки?
Я пытаюсь определить, могу ли я использовать ManualResetEvent
здесь, чтобы гарантировать, что в параллельной среде, внутренние действия myMethod()
никогда не вызываются одновременно.
static volatile bool _syncInitialized = false;
static ManualResetEvent _syncEvent = new ManualResetEvent(false);
static object _syncLock = new object();
void myMethod()
{
lock (_syncLock)
{
if (!_syncInitialized) // sync hasn't started, so
{
_syncInitialized = true;
_syncEvent.Set(); // signal for the first time only
}
}
if (_syncEvent.WaitOne()) // wait for signal
{
_syncEvent.Close();
_syncEvent = new ManualResetEvent(false); // reset to false
// actions that should be forced to run sequentially
}
}
РЕДАКТИРОВАТЬ - Обратите внимание, что я использую ManualResetEvent вместо просто lock(), потому что я хочу иметь возможность добавить тайм-аут, потенциально.
1 ответ
У вас есть хотя бы одна возможность для состояния гонки. Рассматривать:
Поток № 1 выполняет _syncEvent.WaitOne()
и успешно, затем выгружается, прежде чем он может выполнить _syncEvent.Close()
, Поток #2 приходит и выполняет WaitOne()
, а также успешно.
Другая проблема в том, что вы звоните Close()
с последующим построением нового экземпляра, по-видимому, в качестве способа сброса. Представьте себе, что вы называете Close()
, поток выгружается, следующий поток приходит и пытается сделать WaitOne()
и выдает исключение, потому что объект был закрыт.
Если вы хотите сбросить событие, позвоните Reset()
,
Вы, вероятно, не можете сделать эту работу с ManualResetEvent
, Как уже говорили другие, ManualResetEvent
используется для сигнализации, а не взаимного исключения.
Вы говорите, что хотите внедрить таймаут в будущем. Если вы просто хотите, чтобы поток ожидал блокировки в течение некоторого периода времени, а затем завершил работу, если он не может получить блокировку, используйте одну из перегрузок Monitor.TryEnter, которые принимают значение времени ожидания. Например:
private object _syncObject = new Object();
void MyMethod()
{
if (!Monitor.TryEnter(_syncObject, TimeSpan.FromSeconds(5)))
{
return; // couldn't get the lock
}
try
{
// got the lock. Do stuff here
}
finally
{
Monitor.Exit(); // release the lock
}
}
Есть некоторые споры о том, действительно ли вы хотите снять блокировку в finally
, Если код выдает исключение, то возможно (вероятно?), Что ресурс, который вы защищали, сейчас находится в неполном или иным образом поврежденном состоянии. В этом случае вы можете не захотеть, чтобы другие потоки воздействовали на него. Снимите ли вы блокировку с учетом исключений - это дизайнерское решение, которое вам придется принять в соответствии с требованиями вашего приложения.