AutoResetEvent 的诡异行为

简介: 一.缘起        最近做一个服务端程序,系统运行时,在特定的时候会启动一个通知线程,通知线程执行的方法经简化后就是如下的FirstStateNotifyThread: AutoResetEvent autoResetEvent = new AutoResetEvent(fal...

 一.缘起

       最近做一个服务端程序,系统运行时,在特定的时候会启动一个通知线程,通知线程执行的方法经简化后就是如下的FirstStateNotifyThread:

    AutoResetEvent autoResetEvent = new AutoResetEvent(false);
    private void FirstStateNotifyThread()
    {
        this.logger.LogWithTime("进入通知线程");            
        if (this.autoResetEvent.WaitOne(this.timeoutInMsecs))
        {
            //......
        }
        else
        {
            //......
        }
    }

      通知线程中用到了AutoResetEvent以等待某个事件完成以达到同步的目的。启动线程的方法如下:

   CbGeneric cb = new CbGeneric(this.FirstStateNotifyThread);
   cb.BeginInvoke(null, null);

      开发、调试、测试、部署到自己的测试服务器 都一切运行正常。当部署到正式的服务器上运行时,发现需要启动线程的时刻到来时,没有出现“进入通知线程”的日志,即FirstStateNotifyThread方法没有被执行,线程没有被启动。

 

二.追踪

      于是,我换了一种启动线程的方式,像下面这样:

   Thread thread = new Thread(new ThreadStart(this.FirstStateNotifyThread));
   thread.Start();

      情况不仅依旧,而且当要启动线程时,整个进程异常退出了,弹出的提示框内容是“程序遇到问题,将被关闭”。

      然后,我再换一种方式:

   ThreadPool.QueueUserWorkItem(new WaitCallback(this.FirstStateNotifyThread1) ;

      仍然一样,也导致进程退出。
      郁闷了,并且只有这台服务器上才会出现,其也是windows 2003 server 系统,是怎么回事了?

      接着,我把FirstStateNotifyThread 方法中的逻辑代码全部去掉,只留一句写日志的代码,结果,可以正常执行。就这样不断地增加业务代码,最后问题定位到了autoResetEvent.WaitOne方法,如果注释掉这一句,就OK,开启这一句,就导致执行FirstStateNotifyThread 的线程无法启动。

      大概找到问题的位置后,我尝试使用AutoResetEvent的另一个WaitOne重载方法:

  autoResetEvent.WaitOne(this.timeoutInMsecs ,false)

      使用这个重载方法后,在正式的服务器上也可以顺利的启动FirstStateNotifyThread 线程了。

 

三.暂时的结论

      问题看似解决了,但是问题的根源在哪里了?我用reflector查看了AutoResetEvent的WaitOne方法的源码,一起来看看:

[TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")]
public virtual bool WaitOne(int millisecondsTimeout)
{
    return this.WaitOne(millisecondsTimeout, false);
}

public virtual bool WaitOne(int millisecondsTimeout, bool exitContext)
{
    if (millisecondsTimeout < -1) throw new ArgumentOutOfRangeException("millisecondsTimeout", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegOrNegative1"));
    return this.WaitOne((long) millisecondsTimeout, exitContext);
}

      第一个WaitOne方法直接调用了第二个重载的WaitOne方法,这没什么问题。焦点在于第一个WaitOne方法标记了TargetedPatchingOptOut这样一个Attribute,查询MSDN知道:TargetedPatchingOptOut是用于指示内联(inline),熟悉C或C++的朋友对这个词应该非常熟悉。根据前面的步步验证,可以肯定的是,在我们正式的服务器上要加载或执行内联了WaitOne的代码镜像时,出现了异常。至于是什么异常,代码中使用try/catch捕获不到。

      之后,我又测试了ManualResetEvent,也存在同样的情况。问题的根源可能已经涉及到了CLR或windows程序执行,但还是可以总结一点经验:为了使ManualResetEvent/AutoResetEvent在所有的机器上都能正常运转,请使用带有两个参数的WaitOne方法。

      有哪位朋友能知道更多内幕原因的,还望留言不吝赐教,谢谢。 

 

目录
相关文章
|
3月前
|
监控 安全 IDE
别再瞎用了!synchronized的正确使用姿势在这里!
别再瞎用了!synchronized的正确使用姿势在这里!
90 4
|
6月前
获取线程号和杀不死的NSThread线程
获取线程号和杀不死的NSThread线程
35 0
|
6月前
|
C#
C# | 使用AutoResetEvent和ManualResetEvent进行线程同步和通信
在多线程编程中,AutoResetEvent 和 ManualResetEvent 是两个常用的同步原语。它们用于线程间的通信和协调,以确保线程按照特定的顺序执行。本篇博客将介绍这两种同步原语的概念、用法和区别。
114 0
C# | 使用AutoResetEvent和ManualResetEvent进行线程同步和通信
无聊小知识.03 wait(),notify()虚假唤醒
无聊小知识.03 wait(),notify()虚假唤醒
138 0
无聊小知识.03 wait(),notify()虚假唤醒
加了一个synchronized锁,程序就崩了
加了一个synchronized锁,程序就崩了
|
存储 Java C++
死磕synchronized一:synchronized修饰的方法的执行
近期准备写一个专栏:从Hotspot源码角度剖析synchronized。前前后后大概有10篇,会全网发,写完后整理成电子书放公众号供大家下载。对本专栏感兴趣的、希望彻彻底底学明白synchronized的小伙伴可以关注一波。电子书整理好了会通过公众号群发告知大家。我的公众号:硬核子牙。
159 0
死磕synchronized一:synchronized修饰的方法的执行
|
监控 Java 数据库
一个线程罢工的诡异事件
线上某个应用里业务逻辑没有执行,导致的结果是数据库里的某些数据没有更新。
|
Java
一个线程中断的Bug:明明中断了线程,却为何不起作用呢?
一个线程中断的Bug:当我们在调用Java对象的wait()方法或者线程的sleep()方法时,需要捕获并处理InterruptedException异常。如果我们对InterruptedException异常处理不当,则会发生我们意想不到的后果!今天,我们就以一个案例的形式,来为大家详细介绍下为何中断执行的线程不起作用。
418 0
一个线程中断的Bug:明明中断了线程,却为何不起作用呢?
线程中sleep导致崩溃是一种错觉
线程中sleep导致崩溃是一种错觉
180 0
C#深入理解AutoResetEvent和ManualResetEvent
当在C#使用多线程时就免不了使用AutoResetEvent和ManualResetEvent类,可以理解这两个类可以通过设置信号来让线程停下来或让线程重新启动,其实与操作系统里的信号量很相似(汗,考完考试已经有点忘记了)。
1934 0