善意提醒

如果您打开本站很慢,布局排版混乱,并且看不到图片,那么可能是因为您还没有掌握用科学的方法上网的本领。

2014-10-31

SingleThread 下遇到的并发问题

手下某个小弟,有一天报告我说他写的某个 Win32 Application 有一个奇怪的 Bug,搞了半天搞不定,向我寻求支援。Bug 现象是:下载文件,完毕弹框提示,点掉之后报错,Crash。

通常而言,这种问题,往往是因为在释放、删除什么东西的时候,该做的事情没做对,比如对着一个对象的指针进行了重复 delete 之类。但看了下代码,没觉得这方面有什么问题。因为这是个 SingleThread 的程序,于是尝试用单步跟踪跟了一下,发现有一段代码似乎在所属对象析构之后还在跑。这就有点奇怪了:SingleThread 的 Application,不应该有这种属于 MultiThread 的毛病才对。Socket 模型用的是 AsyncSelect,也就是说「异步」是用 Windows 消息做出来的,并不是真的「并发」。那么到底是哪里不对劲呢?

再接下来分析发现,虽然是 SingleThread,但最后出错前弹的那个提示框,是在 OnReceive 的时候通过 SendMessage 去弹的。这样就有眉目了:ModalDialog 并不阻塞 ParentWindow 的消息循环,所以在弹框等待用户确认的时候,消息循环收到了 OnClose,于是 Socket 对象在用户点击确认按钮之前,其实已经被 Destroy 了。之前还没跑完的 OnReceive,接着再跑的话,当然只能 Crash 了。

分析到这里,问题就已经很明白了:这就跟 MultiThread 下临界区没加锁一样嘛。你以为 SingleThread 下每个函数就都是原子操作,不会被乱入的东西打搅?呵呵,你一 DoModal 就会给你再嵌个消息循环进去的。可怜很多小弟连 DoModel 的原理都没搞懂就开始写程序了。我上次还听几个小弟在争论相关问题呢。不是说写程序必须啥都弄明白才能开始,但若是只拎半壶水就开跑,将来就难免会碰上这种「奇怪」的问题。

要修正这个问题,也很简单,改成用 PostMessage 让 MainWindow 自己去处理弹框的事情就可以了。不过有点奇怪的是,在 XP 下好像不会看到错误现象。Win7 下直接运行 EXE 也不报错。只有通过两层以上的 CreateProcess 去调用,才会看到现象。难怪没什么用户报告这个问题。是不是 OS 觉得这个 EXE 反正会很快地 Over 掉,有些错误就不报算了?看来微软在私底下还是有一些没告诉大家的小动作的哈哈。

总结一下,这个案例教育我们:
  1. 不要以为只要是 SingleThread 就一定不会遇到并发问题。
  2. 前/后台逻辑应该要区分清晰,是后台代码就别抢前台的活儿。
  3. 还有,SendMessage / PostMessage 不要不经大脑就乱用。

没有评论:

发表评论