一个老系统的问题,用的system v消息队列同步等响应,通过alarm信号来进行超时控制。现在系统进行升级改造(所谓云化),原来进程处理的逻辑全部改成了线程框架,问题就出现了。alarm信号发出的时候,到底哪个线程会接收到这个信号呢?

  于是赶忙问了下百度,有些地方说随机的,有些地方解答说随机的;另外有些人推荐用pthread_sigmask和sgwait之类,云者颇多。还有些同仁推荐改用异步消息队列,在没有消息的时候进行usleep、nanosleep,posix消息队列等等。

  最后用了usleep,之所以不用另外的,我一个一个说个大概:

  pthread_sigmask,这个方式试了下,我们提供的是客户端,也就是给别人的lib。这些依赖于调用进程主动做一些事情,出了问题排查比较难。试了一段时间发现,对宿主进程的改造比较多,定位也没玩没了,因为别人也是一个经历了十几年没有重构的老系统了。

  posix消息队列,这个是支持异步的。但是整个团队都是延续着system v的使用习惯,在核心流程里面直接改成posix会影响维护的压力

  消息队列nowait,然后usleep。网上很多文章说usleep有着这样那样的问题(不准确,线程不安全,影响信号一堆啥的)

  所以我还测试了下(代码就不贴了,比较多),如下:    OS版本Linux 2.6.32-358.el6.x86_64

function time(usec) realTime reduce
-------------------------------------------------------------------
usleep 500000 500138 138
nanosleep 500000 500139 139
select 500000 500591 591
usleep 100000 100133 133
nanosleep 100000 100129 129
select 100000 100147 147
usleep 50000 50132 132
nanosleep 50000 50128 128
select 50000 50130 130
usleep 10000 10130 130
nanosleep 10000 10107 107
select 10000 10129 129
usleep 1000 1077 77
nanosleep 1000 1064 64
select 1000 1079 79
usleep 900 971 71
nanosleep 900 973 73
select 900 971 71
usleep 500 570 70
nanosleep 500 563 63
select 500 571 71
usleep 100 168 68
nanosleep 100 168 68
select 100 158 58
usleep 10 68 58
nanosleep 10 67 57
select 10 67 57
usleep 1 60 59
nanosleep 1 57 56
select 1 58 57

  结果显示,usleep500网上,基本误差还是很小的,往下的话就很不精确了。

  扯了这多和主题无关的,这里是分割线=============================================

  上面也提到了,我中间经历了使用pthread_sigmask,但是有问题,基于对线程信号的陌生。我写了一个测试程序看看,代码如下:

#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <errno.h>
#include <string.h>

#define TH_NUM 10

volatile int signflag = 0;
volatile pthread_t pid = 0;
volatile int num = 3;
pthread_t t_id[TH_NUM];

void sigalarm(int sign)
{
signflag = sign;
pid = pthread_self();
return;
}

void sigmaskhandleset(int sign, void (*handle)(int))
{
struct sigaction new_sigaction;
struct sigaction old_sigaction;

memset(&new_sigaction, 0x00, sizeof(new_sigaction));
memset(&old_sigaction, 0x00, sizeof(old_sigaction));

sigemptyset(&new_sigaction.sa_mask);
sigfillset(&new_sigaction.sa_mask);
new_sigaction.sa_handler = handle;

sigaction(sign, &new_sigaction, &old_sigaction);
return ;
}

void* thread_proc_1(void* pMgr)
{
// usleep(200);
printf("thread_proc_1() my self thread_id : %ld\n", pthread_self());
sigset_t set;
sigemptyset(&set);
sigaddset(&set, SIGALRM);
pthread_sigmask(SIG_UNBLOCK, &set, NULL);

sigmaskhandleset(SIGALRM, sigalarm);

// usleep(1000000);
sleep(20);
printf("thread_proc_1 speak : i am ready\n");

printf("thread_proc_1's signflag : %d , pid : %ld\n", signflag, pid);
num--;
return NULL;
}

void* thread_proc_2(void* pMgr)
{
printf("thread_proc_2() my self thread_id : %ld\n", pthread_self());
sigset_t set;
sigemptyset(&set);
sigaddset(&set, SIGALRM);
pthread_sigmask(SIG_UNBLOCK, &set, NULL);

sigmaskhandleset(SIGALRM, sigalarm);

// usleep(1000);
sleep(30);
printf("thread_proc_2 speak : i am ready\n");

printf("thread_proc_2's signflag : %d , pid : %ld\n", signflag, pid);
num--;
return NULL;
}

void* thread_alarm_post(void* pMgr)
{
// kill(getpid(), SIGALRM);
pthread_kill(t_id[1], SIGALRM);
alarm(1);
printf("thread_alarm_post speak : i has post alarm\n");
num--;
return NULL;
}

int main()
{
int ret = 0;
pthread_attr_t pthread_attr;

sigset_t set;
sigemptyset(&set);
sigaddset(&set, SIGALRM);
sigprocmask(SIG_BLOCK, &set, NULL);
// sigmaskhandleset(SIGALRM, sigalarm);
printf("main speak : i am ready\n");

pthread_attr_init(&pthread_attr);
pthread_attr_setdetachstate(&pthread_attr, PTHREAD_CREATE_DETACHED);
ret = pthread_create(&t_id[0], &pthread_attr, thread_proc_1, NULL);
if (0 != ret) {
printf("pthread_create failed : %d\n", errno);
return 0;
}

ret = pthread_create(&t_id[1], &pthread_attr, thread_proc_2, NULL);
if (0 != ret) {
printf("pthread_create failed : %d\n", errno);
return 0;
}

printf("main() my self thread_id : %ld\n", pthread_self());

ret = pthread_create(&t_id[2], &pthread_attr, thread_alarm_post, NULL);
if (0 != ret) {
printf("pthread_create failed : %d\n", errno);
return 0;
}

// kill(getpid(), SIGALRM);
// pthread_join(t_id[0], NULL);
// pthread_join(t_id[1], NULL);
// pthread_join(t_id[2], NULL);
// while(num>0);
return 0;
}

   各种情况都测试了下,里面的注释掉那部分其实也是测试过程中的一部分。总结了下(基于测试的OS版本,不针对其它OS):

    1、使用过程中看不出来usleep、sleep会产生SGIALRM信号(针对网上说的usleep会影响信号这一方面验证的)

    2、过程中看不出来usleep、sleep会影响线程之间的安全(针对网上说的usleep线程不安全)

    3、信号先被哪个线程接收?我还是无法确定,也有可能是随机的。

      但是可以确定的是linux下肯定不是最后一个注册信号处理函数的那个线程

      我觉得也不是正在运行的那个线程,道理很简单,因为如果主线程不屏蔽信号,那么一直都是主线程处理信号

      否则的话,一直都是第一个线程先处理

    4、我测试了几百遍同样一个程序(没有改代码重编的情况下),都是pthread_self()最大的那个线程处理了信号响应。这能说明什么问题吗?

      我不能这么下结论,但是他应该说明了些什么东西!

今天又讨论起来这个问题,重新做了一次测试!补充一些上次疏漏的地方:

    1、测试结果还是结果还是最大的线程接收,也就是说如果主线程没有屏蔽!那么处理信号中断函数的pthread_self()函数返回的都是主线程的id

    2、但是每次中断不确定是主线程,而有可能是其它子线程!

    3、线程在sleep过程中,每次被中断之后并不是立刻返回!而是不可预测的中断次数之后返回了

    4、pthread_kill函数每次都会及时中断,而且这个过程中sleep会里面返回

linux 多线程 信号的更多相关文章

  1. Linux中多线程信号的处理

    1. 博文:Linux多线程中使用信号-1  http://blog.csdn.net/qq276592716/article/details/7325250 2. 博文:Linux多线程信号总结  ...

  2. Linux操作系统多线程信号总结

    linux 多线程信号编程总结 linux 多线程信号总结(一) 1. 在多线程环境下,产生的信号是传递给整个进程的,一般而言,所有线程都有机会收到这个信号,进程在收到信号的的线程上下文执行信号处理函 ...

  3. linux 多线程信号处理总结

    linux 多线程信号总结(一) 1. 在多线程环境下,产生的信号是传递给整个进程的,一般而言,所有线程都有机会收到这个信号,进程在收到信号的的线程上下文执行信号处理函数,具体是哪个线程执行的难以获知 ...

  4. 《Linux多线程服务端编程:使用muduo C++网络库》上市半年重印两次,总印数达到了9000册

    <Linux多线程服务端编程:使用muduo C++网络库>这本书自今年一月上市以来,半年之内已经重印两次(加上首印,一共是三次印刷),总印数达到了9000册,这在技术书里已经算是相当不错 ...

  5. [转]一个简单的Linux多线程例子 带你洞悉互斥量 信号量 条件变量编程

    一个简单的Linux多线程例子 带你洞悉互斥量 信号量 条件变量编程 希望此文能给初学多线程编程的朋友带来帮助,也希望牛人多多指出错误. 另外感谢以下链接的作者给予,给我的学习带来了很大帮助 http ...

  6. Linux 多线程应用中如何编写安全的信号处理函数

    http://blog.163.com/he_junwei/blog/static/1979376462014021105242552/ http://www.ibm.com/developerwor ...

  7. LINUX多线程(一)(创建和退出)

    1. Linux多线程概述 1.1. 概述 进程是系统中程序执行和资源分配的基本单位.每个进程有自己的数据段.代码段和堆栈段.这就造成进程在进行切换等操作时都需要有比较负责的上下文切换等动作.为了进一 ...

  8. Linux多线程编程和Linux 2.6下的NPTL

    Linux多线程编程和Linux 2.6下的NPTL 在Linux 上,从内核角度而言,基本没有什么线程和进程的区别--大家都是进程.一个进程的多个线程只是多个特殊的进程他们虽然有各自的进程描述结构, ...

  9. Linux多线程编程小结

     Linux多线程编程小结 前一段时间由于开题的事情一直耽搁了我搞Linux的进度,搞的我之前学的东西都遗忘了,非常烦躁的说,如今抽个时间把之前所学的做个小节.文章内容主要总结于<Linux程序 ...

随机推荐

  1. 我想solo自己一个人!

    区域赛之后你就该走了,现在你告诉我,没精力不打了,我真谢谢你! 今年就TM的没有一点舒心的地方! 父母分居, 队友出走, 队伍解散, 白天家里两个外甥很吵, 鼻窦炎复发, 喜欢的妹子也追不到, 整夜失 ...

  2. python json.dumps中ensure_ascii的使用,load与loads的区别

    json模块最常用的两个功能: 一:json.dumps(),用于将dict拆分成str格式,称为序列化,注意序列化后,虽然print出来仍然显示的字典的样子,但是此时已经是str类型了. 其中,有时 ...

  3. python的unittest框架中的assert断言

    unittest框架自带断言,如果想用assert断言,一定要引入unittest.TestCase框架才行,不然不会自动识别assert断言

  4. 70行实现Promise核心源码

    70行实现Promise核心源码 前言: ​ 一直以来都是只会调用Promise的API,而且调API还是调用axios封装好的Promise,太丢人了!!!没有真正的去了解过它的原理是如何实现的,自 ...

  5. 编译原理-第四章 语法分析-4.7 规范的LR分析

    规范的LR分析 一.规范LR(l)项 二.规范LR(l)项集族 1.构建项目集 2.例 三.规范LR(1)语法分析表 1.构造 2.例1 3.例2 四.LALR语法分析表 1.重要性 2.特点 3.构 ...

  6. 【2020Java最新学习路线】写了很久,这是一份最适合普通大众、科班、非科班的路线

    点赞再看,养成习惯,微信搜索[三太子敖丙]关注这个互联网苟且偷生的工具人. 本文 GitHub https://github.com/JavaFamily 已收录,有一线大厂面试完整考点.资料以及我的 ...

  7. Linux之V4L2基础编程

    Linux之V4L2基础编程 本文内容来源于网络,本博客进行整理. 1. 定义 V4L2(Video For Linux Two) 是内核提供给应用程序访问音.视频驱动的统一接口. 2. 工作流程: ...

  8. 组合模式(c++实现)

    组合模式 目录 组合模式 定义 动机 UML类图 场景拆解 源码实现 优点 缺点 定义 将对象组合成树形结构以表示"部分-整体"的层次结构.组合模式是的用户对单个对象和组合对象的使 ...

  9. 解决Hystrix dashboard Turbine 一直 Loading…… 及其他坑

    问题一.请求 /hystrix.stream 报错,我这里以端口9001为例 请求 http://localhost:9001/hystrix.stream 报404 是因为Srping Boot 2 ...

  10. tomcat 8.5 及其 9.0 response写cookie 设置damain为 [.test.com] 出错 An invalid domain [.test.com] was specified for this cookie

    抛出异常: java.lang.IllegalArgumentException: An invalid domain [.test.com] was specified for this cooki ...