简体   繁体   English

C#事件处理:避免线程争用和线程池耗尽的最佳实践

[英]c# event handling: best practice to avoid thread contention and threadpool draining

When events trigger, they use threads from the threadpool. 事件触发时,它们使用线程池中的线程。 So if you have a bunch of events that trigger faster than they return, you drain your threadpool. 因此,如果您有一堆事件触发得比返回事件快,那么就会耗尽线程池。 So whenever you have an event handler method that doesn't have any other control to limit the rate of threads entering, and doesn't have any guarantee of returning quickly, and you're not painstakingly implementing 100% thread-safe code inside that method, it's probably best to implement some thread control. 因此,只要您有一个事件处理程序方法,该方法没有任何其他控件来限制线程进入的速率,并且不能保证快速返回,并且您不必费心地在其中实现100%线程安全的代码方法,最好是实现一些线程控制。 The obvious simple thing to do would be to lock() inside the event handling method, but if you do that, all the threads after the first one will block in queue, waiting to enter the lock region, hogging all your threads from threadpool. 显而易见,最简单的方法是将事件处理方法内的lock()锁定,但是如果这样做,则第一个线程之后的所有线程都将在队列中阻塞,等待进入锁定区域,从而从线程池中占用所有线程。 It is probably better to detect another thread is inside this method, and quickly abort instead. 最好检测此方法内部有另一个线程,然后迅速中止。

The question is: I have a way of detecting another thread already running, and quickly aborting the subsequent threads. 问题是:我有一种方法可以检测到另一个正在运行的线程,并快速中止后续线程。 But it doesn't seem very C#-ish due to the use of "const" and manually handling a locking flag at a low level. 但是由于使用“ const”并在较低级别上手动处理锁定标志,因此它看起来不太像C#。 Is there a better way? 有没有更好的办法?

This is basically a direct replication of the lock() functionality, but using a non-blocking Interlocked.Exchange, instead of using the blocking Monitor.Enter() 这基本上是lock()功能的直接复制,但是使用非阻塞的Interlocked.Exchange,而不是使用阻塞的Monitor.Enter()

    public class FooGoo
    {
        private const int LOCKED = 0;            // could use any arbitrary value; I choose 0
        private const int UNLOCKED = LOCKED + 1; // any arbitrary value, != LOCKED
        private static int _myLock = UNLOCKED;
        void myEventHandler()
        {
            int previousValue = Interlocked.Exchange(ref _myLock, LOCKED);
            if ( previousValue == UNLOCKED )
            {
                try
                {
                    // some handling code, which may or may not return quickly
                    // maybe not threadsafe
                }
                finally
                {
                    _myLock = UNLOCKED;
                }
            }
            else
            {
                // another thread is executing right now. So I will abort.
                //
                // optional and environment-specific, maybe you want to 
                // queue some event information or set a flag or something,
                // so you remember later that this thread aborted
            }
        }
    }

So far, this is the best answer I have found. 到目前为止,这是我找到的最佳答案。 Does there exist any shorthand equivalent of a non-blocking lock() to shorten this up? 是否存在等效的非阻塞lock()的简化形式?

static object _myLock;
void myMethod ()
{
    if ( Monitor.TryEnter(_myLock) )
    {
        try
        {
            // Do stuff
        }
        finally
        {
            Monitor.Exit(_myLock);
        }
    }
    else
    {
        // then I failed to get the lock.  Optionally do stuff.
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM