【问题标题】:c# event handling: best practice to avoid thread contention and threadpool drainingc# 事件处理:避免线程争用和线程池耗尽的最佳实践
【发布时间】:2013-02-03 00:40:48
【问题描述】:

当事件触发时,它们使用线程池中的线程。因此,如果您有一堆事件的触发速度比它们返回的速度快,那么您就会耗尽线程池。因此,只要您有一个事件处理程序方法,该方法没有任何其他控件来限制线程进入的速率,并且不能保证快速返回,并且您不会在其中煞费苦心地实现 100% 线程安全代码方法,最好实现一些线程控制。显而易见的简单事情是在事件处理方法中使用 lock(),但如果这样做,第一个线程之后的所有线程将阻塞在队列中,等待进入锁定区域,从线程池中占用所有线程。最好检测另一个线程在这个方法中,然后快速中止。

问题是:我有一种方法可以检测到另一个已经在运行的线程,并快速中止后续线程。但由于使用“const”并在低级别手动处理锁定标志,它似乎不是很 C#-ish。有没有更好的办法?

这基本上是 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
            }
        }
    }

【问题讨论】:

  • 我刚刚发现:lock() 是 System.Threading.Monitor.Enter(x) 的快捷方式;尝试 { ... } 最后 { System.Threading.Monitor.Exit(x);所以这已经是一个改进,消除了 const。我想剩下的唯一一件事就是问,是否存在任何类型的非阻塞快捷方式,例如 lock() 来缩短上述所有内容?
  • 事件触发使用线程池。
  • @JohnSaunders 至少,由 system.timers.timer 产生的事件在线程池线程上执行,正如查看 Thread.CurrentThread.IsThreadPoolThread 所证实的那样。但无论如何,这个细节并不是问题的核心。
  • 我敢打赌,定时器事件不是在线程池线程上执行的,而是了解定时器到期的 .NET 代码已经在线程池线程上运行。我相信您会发现,通常,事件是在检测到事件的同一线程上引发的。
  • @JohnSaunders 再次,这并不重要,这只是一个切线,但为了测试这一点,我创建了一个新的控制台应用程序,并在 Main() 内部创建了一堆计时器,并且在事件处理方法中,显示有关当前线程的信息。为了防止应用程序死机,我将原始线程置于无限睡眠状态。

标签: c# multithreading events thread-safety threadpool


【解决方案1】:

到目前为止,这是我找到的最佳答案。是否存在等效于非阻塞 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.
    }
}

【讨论】:

    猜你喜欢
    • 2015-02-19
    • 2011-01-19
    • 1970-01-01
    • 1970-01-01
    • 2010-10-14
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多