这个锁+ ManualResetEvent使用线程安全吗? [英] Is this lock + ManualResetEvent usage thread safe?
问题描述
This is a separate question based off of this question. To recap, say I have two functions that manipulate a count, and an OnTimer function that fires at a regular interval. My desire is that if/when OverwriteCount is called, IncrementCount can't be executed until the timer function executes.
建议的解决方案是:
private int _myCount = 0;
private readonly object _sync = new object();
private ManualResetEventSlim mre = new ManualResetEventSlim(initialState: true);
void IncrementCount()
{
mre.Wait(); // all threads wait until the event is signaled
lock (_sync)
{
_myCount++;
}
}
void OverwriteCount(int newValue)
{
lock (_sync)
{
mre.Reset(); // unsignal the event, blocking threads
_myCount = newValue;
}
}
void OnTimer()
{
lock (_sync)
{
Console.WriteLine(_myCount);
mre.Set(); // signal the event
}
}
ManualResetEventSlim尝试确保一旦OverwriteCount()解除事件的信号,对_myCount的任何修改都必须等到OnTimer()执行之后.
The ManualResetEventSlim tries to ensure that once OverwriteCount() unsignals the event, any modifications to _myCount must wait until OnTimer() executes.
问题:
- 说线程A输入IncrementCount()并传递事件的wait()-已经指示了ManualResetEvent的初始状态. 然后,
- 线程B启动并执行所有OverwriteCount().
- 然后,线程A通过获取锁并递增_myCount来继续.
这违反了我的目标,因为_myCount会在OnTimer运行之前在调用OverwriteCount()之后更改.
This violates my goal as _myCount would change after a call to OverwriteCount(), prior to OnTimer running.
被拒绝的选择:我可以将mre.Wait()移入lock(_sync)内,但这会带来死锁风险.如果线程A调用IncrementCount()并在等待时阻塞,则其他线程都无法获取该锁来释放它.
Rejected Alternative: I could move mre.Wait() within lock(_sync) but that poses a deadlock risk. If thread A calls IncrementCount() and blocks on the wait, no other threads can acquire the lock to release it.
问题:我是否需要其他同步原语才能实现目标?或者,我是否对线程安全性有疑问?
Question: Do I need a different synchronization primitive to achieve my goal? Alternatively, am I wrong about the thread safety concern?
推荐答案
我认为您只需使用标准的Monitor
和附加标志即可实现您的目标.
I think you can achieve your goal with just the standard Monitor
and an additional flag.
private readonly object _sync = new object();
private int _myCount = 0;
private bool _canIncrement = true;
void IncrementCount()
{
lock (_sync)
{
// If the flag indicates we can't increment, unlock _sync and wait for a pulse.
// Use a loop here to ensure that if Wait() returns following the PulseAll() below
// (after re-acquiring the lock on _sync), but a call to OverwriteCount managed to
// occur in-between, that we wait again.
while (!_canIncrement)
{
Monitor.Wait(_sync);
}
_myCount++;
}
}
void OverwriteCount(int newValue)
{
lock (_sync)
{
_canIncrement = false;
_myCount = newValue;
}
}
void OnTimer()
{
lock (_sync)
{
Console.WriteLine(_myCount);
_canIncrement = true;
// Ready any threads waiting on _sync in IncrementCount() above
Monitor.PulseAll(_sync);
}
}
这篇关于这个锁+ ManualResetEvent使用线程安全吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!