linux 多線程 信號

  一個老系統的問題,用的system v消息隊列同步等響應,經過alarm信號來進行超時控制。如今系統進行升級改造(所謂雲化),原來進程處理的邏輯所有改爲了線程框架,問題就出現了。alarm信號發出的時候,到底哪一個線程會接收到這個信號呢?linux

  因而趕緊問了下百度,有些地方說隨機的,有些地方解答說隨機的;另外有些人推薦用pthread_sigmask和sgwait之類,雲者頗多。還有些同仁推薦改用異步消息隊列,在沒有消息的時候進行usleep、nanosleep,posix消息隊列等等。安全

  最後用了usleep,之因此不用另外的,我一個一個說個大概:框架

  pthread_sigmask,這個方式試了下,咱們提供的是客戶端,也就是給別人的lib。這些依賴於調用進程主動作一些事情,出了問題排查比較難。試了一段時間發現,對宿主進程的改造比較多,定位也沒玩沒了,由於別人也是一個經歷了十幾年沒有重構的老系統了。異步

  posix消息隊列,這個是支持異步的。可是整個團隊都是延續着system v的使用習慣,在覈心流程裏面直接改爲posix會影響維護的壓力函數

  消息隊列nowait,而後usleep。網上不少文章說usleep有着這樣那樣的問題(不許確,線程不安全,影響信號一堆啥的)post

  因此我還測試了下(代碼就不貼了,比較多),以下:    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):

    一、使用過程當中看不出來usleep、sleep會產生SGIALRM信號(針對網上說的usleep會影響信號這一方面驗證的)

    二、過程當中看不出來usleep、sleep會影響線程之間的安全(針對網上說的usleep線程不安全)

    三、信號先被哪一個線程接收?我仍是沒法肯定,也有多是隨機的。

      可是能夠肯定的是linux下確定不是最後一個註冊信號處理函數的那個線程

      我以爲也不是正在運行的那個線程,道理很簡單,由於若是主線程不屏蔽信號,那麼一直都是主線程處理信號

      不然的話,一直都是第一個線程先處理

    四、我測試了幾百遍一樣一個程序(沒有改代碼重編的狀況下),都是pthread_self()最大的那個線程處理了信號響應。這能說明什麼問題嗎?

      我不能這麼下結論,可是他應該說明了些什麼東西!

 今天又討論起來這個問題,從新作了一次測試!補充一些上次疏漏的地方:

    一、測試結果仍是結果仍是最大的線程接收,也就是說若是主線程沒有屏蔽!那麼處理信號中斷函數的pthread_self()函數返回的都是主線程的id

    二、可是每次中斷不肯定是主線程,而有多是其它子線程!

    三、線程在sleep過程當中,每次被中斷以後並非馬上返回!而是不可預測的中斷次數以後返回了

    四、pthread_kill函數每次都會及時中斷,並且這個過程當中sleep會裏面返回

相關文章
相關標籤/搜索