标签 C 下的文章

慎用线程取消

本周二,我们产品在某省的一个节点应用运行时出现了“死锁”情况,由于监控得力,我们在“死锁”后一分钟内就发现了这个情况,并及时重启了这个节点应用。由于是集群式系统,一个节点的故障对整个系统业务的运行几乎没造成什么影响。不过,这确是一个潜在的隐患。

经过对系统当时运行日志的分析,我们将问题锁定在“线程取消”这个机制的使用上。在“生产者-消费者”实现思路这篇文章中,我曾经提到过我们目前采用的一种通知机制的实现。消费者进程的主线程创建一个子线程,后者一般挂起在条件变量上等待生产者侧的唤醒。一般情况下,这种机制运行都很良好,问题出就出在消费者进程要退出的时候。

这个机制的实现也是逐渐“改”过来的。最初发现消费者进程退出时子线程长时间无法被唤醒导致无法及时退出,主线程因为要Join子线程,所以也阻塞在Join上,两个线程都挂起了,进程也就无法退出,导致后续业务逻辑上会出现一些问题。

之前开发人员在解决这个问题上采用了“线程取消”机制,在主线程Join子线程前调用pthread_cancel取消了子线程。但由于对线程取消机制理解的不透彻,导致子线程在pthread_cond_wait这个"cancellation point"(man cancellation)上退出。在Sun官方文档中提到在pthread_cond_wait这个取消点退出线程时,线程仍然持有与条件变量关联的那把互斥锁,这样就会导致其他进程在上锁时挂起在互斥锁上。但由于我们在代码中使用了不可移植的死锁恢复机制,这个问题也就不那么明显,偶尔出现(锁状态不一致很可能会导致死锁恢复机制失效),就这个偶尔出现导致了上述问题。

与另外一个产品线的同事做了一下内部沟通,发现他们那边的产品已经做了改善(或许是我们没有经常性同步库代码导致代码出现不一致了^_^)。最初他们通过调用pthread_cleanup_push注册取消点清理程序来完成mutex的unlock,该问题得到了暂时解决。但是子线程在其内部其他取消点的退出也带来的一些麻烦,比如open日志文件时。为了控制子线程在合适的取消点退出,他们采用了Disable Cancel State的线程设置,并在关键路径上使用“enable cancel -> pthread_testcancel -> disable cancel”来设置子线程退出的窗口。

另外为了子线程能在主线程Cancel它的时候有机会被唤醒,主线程在cancel调用后,使用pthread_cond_broadcast给子线程提供了一次机会。当然这也让阻塞在同一个条件变量上的其他线程被“假唤醒”,但这种情况是可以被忍受的。

在很多讲解多线程的书籍中都不建议使用cancel机制,这里也建议慎用。直到目前也许还有一些例外情况我们还没能考虑周全呢。

也谈Configure脚本问题的解决

开了一个下午的技术交流会,回到办公室时离下班时间已经不远,天气预报说今晚有暴雪,外面阴沉的天气似乎也证实了这一点。这时一个同事遇到了一个软件包编译的问题,一时无法解决,向我求助。

这是一个libmemcached的编译问题,我们用的是libmemcached 0.34版本,我的同事在PC Solaris上执行libmemcached的configure脚本时遇到如下错,Configure脚本提示:

checking for pthread-config… no
configure: error: could not find libpthread

但经过确认系统中明明在/usr/lib下有pthread相关库的存在:
Tony Bai-[~/libmemcached-0.34]526:>ll /usr/lib|grep pthread
lrwxrwxrwx   1 root     root          26 2009   9月 10 llib-lpthread.ln -> ../../lib/llib-lpthread.ln
lrwxrwxrwx   1 root     root          23 2009   9月 10 llib-lpthread -> ../../lib/llib-lpthread
lrwxrwxrwx   1 root     root          25 2009   9月 10 libpthread.so.1 -> ../../lib/libpthread.so.1*
lrwxrwxrwx   1 root     root          25 2009   9月 10 libpthread.so -> ../../lib/libpthread.so.1*

又确认了一下用户的环境变量设置,LD_LIBRARY_PATH也包含了这些库的目录。

经验告诉我,这个错误是假象,向上翻Configure的输出结果,的确发现些奇怪的Check结果,如下:

checking for ANSI C header files… no
checking for sys/types.h… no
checking for sys/stat.h… no
checking for stdlib.h… no
checking for string.h… no
checking for memory.h… no
checking for strings.h… no
checking for inttypes.h… no
checking for stdint.h… no
checking for unistd.h… no

第一感觉,这怎么可能呢?这些标准C库头文件居然都Check失败了!在网上用“checking for ANSI C header files… no”搜了一下,也没有找到很好的答案。

我对Configure了解也不多,但是还是让我发现了config.log这根救命稻草。config.log这个文件详细地记录了Configure的每一步校验的执行内容和结果,其中对于标准C头文件的Check是这样做的:

configure:4827: checking for ANSI C header files
configure:4857: gcc -c -g -O2 -m64  conftest.c >&5
conftest.c:1: sorry, unimplemented: 64-bit mode not compiled in
configure:4864: $? = 1
configure: failed program was:
| /* confdefs.h.  */
| #define PACKAGE_NAME "libmemcached"
| #define PACKAGE_TARNAME "libmemcached"
| #define PACKAGE_VERSION "0.34"
| #define PACKAGE_STRING "libmemcached 0.34"
| #define PACKAGE_BUGREPORT "http://tangent.org/552/libmemcached.html"
| /* end confdefs.h.  */
| #include
| #include
| #include
| #include
|
| int
| main ()
| {
|
|   ;
|   return 0;
| }
configure:4995: result: no

再往下看,检测sys/types.h等标准库头文件的错误都是:
conftest.c:1: sorry, unimplemented: 64-bit mode not compiled in
configure:5047: $? = 1

看来并非是系统没有包含标准头文件,而是Configure采用了64-bit编译的方法去测试头文件存在的时候出错。随意创建一个testm64.c的源文件,输入:

/* testm64.c */
int main() {
    ;
    return 0;
}
用gcc -g -m64 testm64.c执行编译,得到与之前相同的错误结果:
testm64.c:1: sorry, unimplemented: 64-bit mode not compiled in

查看Gcc版本,发现是3.4.6,突然恍然大悟,这不是之前发现在Solaris 10 for x86上Gcc 64位编译的一个问题吗,在Solaris 10 for x86上如果要进行64位编译,要使用/usr/sfw/bin下的gcc 3.4.3版本,不能用3.4.6版本。

除了更换Gcc之外,如果你想编译32位版本的话,还可以这样来做:修改Configure脚本,打开Configure,将-m64字样全部删除。这样Configure后编译libmemcached就一切顺利了。

以上关于Configure脚本问题的解决方法,有一定的通用性,因此记之。

如发现本站页面被黑,比如:挂载广告、挖矿等恶意代码,请朋友们及时联系我。十分感谢! Go语言第一课 Go语言精进之路1 Go语言精进之路2 Go语言编程指南
商务合作请联系bigwhite.cn AT aliyun.com

欢迎使用邮件订阅我的博客

输入邮箱订阅本站,只要有新文章发布,就会第一时间发送邮件通知你哦!

这里是 Tony Bai的个人Blog,欢迎访问、订阅和留言! 订阅Feed请点击上面图片

如果您觉得这里的文章对您有帮助,请扫描上方二维码进行捐赠 ,加油后的Tony Bai将会为您呈现更多精彩的文章,谢谢!

如果您希望通过微信捐赠,请用微信客户端扫描下方赞赏码:

如果您希望通过比特币或以太币捐赠,可以扫描下方二维码:

比特币:

以太币:

如果您喜欢通过微信浏览本站内容,可以扫描下方二维码,订阅本站官方微信订阅号“iamtonybai”;点击二维码,可直达本人官方微博主页^_^:
本站Powered by Digital Ocean VPS。
选择Digital Ocean VPS主机,即可获得10美元现金充值,可 免费使用两个月哟! 著名主机提供商Linode 10$优惠码:linode10,在 这里注册即可免费获 得。阿里云推荐码: 1WFZ0V立享9折!


View Tony Bai's profile on LinkedIn
DigitalOcean Referral Badge

文章

评论

  • 正在加载...

分类

标签

归档



View My Stats