开发者

Is the AutoResetEvent type an appropriate choice for an atomic switch?

开发者 https://www.devze.com 2023-01-15 04:19 出处:网络
Suppose I am processing a large amount of incoming data from multiple threads. I may want for this data to act as a trigger for a specific action when certain criteria are met. However, the action is

Suppose I am processing a large amount of incoming data from multiple threads. I may want for this data to act as a trigger for a specific action when certain criteria are met. However, the action is not reentrant; so the开发者_开发知识库 same trigger being fired twice in rapid succession should only cause the action to run once.

Using a simple bool flag to indicate whether the action is currently running is not a robust solution, as a race condition could occur and two (or more) threads could still end up running the same action concurrently after all. A bool within a lock statement on a synchronization object would work, of course; but I generally prefer to avoid locking whenever possible*.

Currently what I do in these cases is use an AutoResetEvent as essentially a form of atomic switch. The code typically looks something like this:

if (conditionMet && readyForAction.WaitOne(0))
{
    try
    {
        doAction();
    }
    finally
    {
        readyForAction.Set();
    }
}

This works, but it strikes me that this may be a less-than-ideal use case for the AutoResetEvent class. Would you say this is an appropriate solution to this problem, or would it make more sense to use some other mechanism?


Update: As Jon pointed out, using Monitor.TryEnter as the locking strategy (instead of the simple lock, which I believe is roughly equivalent to Monitor.Enter), is really quite straightforward:

if (conditionMet && Monitor.TryEnter(lockObject))
{
    try
    {
        doAction();
    }
    finally
    {
        Monitor.Exit(lockObject);
    }
}

That said, I'm strongly inclined to go with Henk's idea of using Interlocked. That seems about as simple as it gets.


A simple (and fast) System.Threading.Interlocked.CompareExchange() will do.

//untested
int flag = 0;

if (conditionMet && Interlocked.CompareExchange(ref flag, 1, 0) == 0)
{
    try
    {
        doAction();
    }
    finally
    {
        flag = 0; // no need for locking
    }
}


"I generally prefer to avoid locking whenever possible" - why? It looks like you're effectively doing something very similar here, but using Windows synchronization primitives instead of CLR ones. You're trying to synchronize so that only one thread can be running a particular piece of code at a time... that certainly sounds like a lock to me. The only difference is that if the lock is already held, you don't want to bother executing the code at all.

You should be able to achieve the same effect using Monitor.TryEnter, which I would personally argue is a bit simpler to understand. (Then again, I "grew up" with Java monitors, so it's closer to my background.)

0

精彩评论

暂无评论...
验证码 换一张
取 消