pthread_kill(pthread_kill kill区别)

http://www.itjxue.com  2023-02-20 02:54  来源:未知  点击次数: 

如何解决正在中止线程

首先我们来看一下,让当前正在运行的子线程停止的所有方法

1.任何一个线程调用exit

2.pthread_exit

3.pthread_kill

4.pthread_cancel

下面我们一一分析各种终止正在运行的程序的方法

任何一个线程调用exit

任何一个线程只要调用了exit都会导致进程结束,各种子线程当然也能很好的结束了,可是这种退出会有一个资源释放的问题.我们知道当一个进程终止时,内核对该进程所有尚未关闭的文件描述符调用close关闭,所以即使用户程序不调用close,在终止时内核也会自动关闭它打开的所有文件。没错,标准C++ IO流也会很好的在exit退出时得到flush并且释放资源,这些东西并不会造成资源的浪费(系统调用main函数入口类似于exit(main(argc,argv))).表面上似乎所有的问题都能随着进程的结束来得到很好的处理,其实并不然,我们程序从堆上分配的内存就不能得到很好的释放,如new ,delete后的存储空间,这些空间进程结束并不会帮你把这部分内存归还给内存.(本文初稿时,因基础不牢固,此处写错,事实上无论进程这样结束,系统都将会释放掉所有代码所申请的资源,无论是堆上的还是栈上的。(感谢ZKey的指导)。这种结束所有线程(包括主线程)的方式实际上在很多时候是非常可取的,但是对于针对关闭时进行一些别的逻辑的处理(指非资源释放逻辑)就不会很好,例如我想在程序被kill掉之前统计一下完成了多少的工作,这个统计类似于MapReduce,需要去每个线程获取,并且最后归并程一个统一的结果等等场景)

如何查看一个进程中的某个线程是否存活

pthread_kill,向线程发送signal,用signal()去抓信号并加上处理函数。

int pthread_kill(pthread_t thread, int sig)

如果int sig的参数不是0,那一定要清楚到底要干什么,而且一定要实现线程的信号处理函数,否则,就会影响整个进程。

如果int sig是0,这是一个保留信号,一个作用是用来判断线程是不是还活着。

使用pthread_kill函数检测一个线程是否还活着的程序:

int?kill_rc?=?pthread_kill(thread_id,0);

if(kill_rc?==?ESRCH)

printf("the?specified?thread?did?not?exists?or?already?quit/n");

else?if(kill_rc?==?EINVAL)

printf("signal?is?invalid/n");

else

printf("the?specified?thread?is?alive/n");

如何正确的终止正在运行的子线程

首先我们来看一下,让当前正在运行的子线程停止的所有方法

1.任何一个线程调用exit

2.pthread_exit

3.pthread_kill

4.pthread_cancel

下面我们一一分析各种终止正在运行的程序的方法

任何一个线程调用exit

任何一个线程只要调用了exit都会导致进程结束,各种子线程当然也能很好的结束了,可是这种退出会有一个资源释放的问题.我们知道当一个进程终止时,内核对该进程所有尚未关闭的文件描述符调用close关闭,所以即使用户程序不调用close,在终止时内核也会自动关闭它打开的所有文件。没错,标准C++ IO流也会很好的在exit退出时得到flush并且释放资源,这些东西并不会造成资源的浪费(系统调用main函数入口类似于exit(main(argc,argv))).表面上似乎所有的问题都能随着进程的结束来得到很好的处理,其实并不然,我们程序从堆上分配的内存就不能得到很好的释放,如new ,delete后的存储空间,这些空间进程结束并不会帮你把这部分内存归还给内存.(本文初稿时,因基础不牢固,此处写错,事实上无论进程这样结束,系统都将会释放掉所有代码所申请的资源,无论是堆上的还是栈上的。(感谢ZKey的指导)。这种结束所有线程(包括主线程)的方式实际上在很多时候是非常可取的,但是对于针对关闭时进行一些别的逻辑的处理(指非资源释放逻辑)就不会很好,例如我想在程序被kill掉之前统计一下完成了多少的工作,这个统计类似于MapReduce,需要去每个线程获取,并且最后归并程一个统一的结果等等场景)

pthread_exit

此函数的使用场景是当前运行的线程运行pthread_exit得到退出,对于各个子线程能够清楚地知道自己在什么时候结束的情景下,非常好用,可是实际上往往很多时候一个线程不能知道知道在什么时候该结束,例如遭遇Ctrl+C时,kill进程时,当然如果排除所有的外界干扰的话,那就让每个线程干完自己的事情后,然后自觉地乖乖的调用pthread_exit就可以了,这并不是本文需要讨论的内容,本文的情景就是讨论如何处理特殊情况。

这里还有一种方法,既然子线程可以通过pthread_exit来正确退出,那么我们可以在遭遇Ctrl+C时,kill进程时处理signal信号,然后分别给在某一个线程可以访问的公共区域存上一个flag变量,线程内部每运行一段时间(很短)来检查一下flag,若发现需要终止自己时,自己调用pthread_exit,此法有一个弱点就是当子线程需要进行阻塞的操作时,可能无暇顾及检查flag,例如socket阻塞操作。如果你的子线程的任务基本没有非阻塞的函数,那么这么干也不失为一种很好的方案。

pthread_kill

不要被这个可怕的邪恶的名字所吓倒,其实pthread_kill并不像他的名字那样威力大,使用之后,你会感觉,他徒有虚名而已

pthread_kill的职责其实只是向指定的线程发送signal信号而已,并没有真正的kill掉一个线程,当然这里需要说明一下,有些信号的默认行为就是exit,那此时你使用pthread_kill发送信号给目标线程,目标线程会根据这个信号的默认行为进行操作,有可能是exit。当然我们同时也可以更改获取某个信号的行为,以此来达到我们终止子线程的目的。

1 #define _MULTI_THREADED

2 #include pthread.h

3 #include stdio.h

4 #include signal.h

5 #include "check.h"

6

7 #define NUMTHREADS 3

8 void sighand(int signo);

9

10 void *threadfunc(void *parm)

11 {

12 pthread_t self = pthread_self();

13 pthread_id_np_t tid;

14 int rc;

15

16 pthread_getunique_np(self, tid);

17 printf("Thread 0x%.8x %.8x entered\n", tid);

18 errno = 0;

19 rc = sleep(30);

20 if (rc != 0 errno == EINTR) {

21 printf("Thread 0x%.8x %.8x got a signal delivered to it\n",

22 tid);

23 return NULL;

24 }

25 printf("Thread 0x%.8x %.8x did not get expected results! rc=%d, errno=%d\n",

26 tid, rc, errno);

27 return NULL;

28 }

29

30 int main(int argc, char **argv)

31 {

32 int rc;

33 int i;

34 struct sigaction actions;

35 pthread_t threads[NUMTHREADS];

36

37 printf("Enter Testcase - %s\n", argv[0]);

38

39 printf("Set up the alarm handler for the process\n");

40 memset(actions, 0, sizeof(actions));

41 sigemptyset(actions.sa_mask);

42 actions.sa_flags = 0;

43 actions.sa_handler = sighand;

44

45 rc = sigaction(SIGALRM,actions,NULL);

46 checkResults("sigaction\n", rc);

47

48 for(i=0; iNUMTHREADS; ++i) {

49 rc = pthread_create(threads[i], NULL, threadfunc, NULL);

50 checkResults("pthread_create()\n", rc);

51 }

52

53 sleep(3);

54 for(i=0; iNUMTHREADS; ++i) {

55 rc = pthread_kill(threads[i], SIGALRM);

56 checkResults("pthread_kill()\n", rc);

57 }

58

59 for(i=0; iNUMTHREADS; ++i) {

60 rc = pthread_join(threads[i], NULL);

61 checkResults("pthread_join()\n", rc);

62 }

63 printf("Main completed\n");

64 return 0;

65 }

66

67 void sighand(int signo)

68 {

69 pthread_t self = pthread_self();

70 pthread_id_np_t tid;

71

72 pthread_getunique_np(self, tid);

73 printf("Thread 0x%.8x %.8x in signal handler\n",

74 tid);

75 return;

76 }

运行输出为:

1 Output:

2

3 Enter Testcase - QP0WTEST/TPKILL0

4 Set up the alarm handler for the process

5 Thread 0x00000000 0000000c entered

6 Thread 0x00000000 0000000d entered

7 Thread 0x00000000 0000000e entered

8 Thread 0x00000000 0000000c in signal handler

9 Thread 0x00000000 0000000c got a signal delivered to it

10 Thread 0x00000000 0000000d in signal handler

11 Thread 0x00000000 0000000d got a signal delivered to it

12 Thread 0x00000000 0000000e in signal handler

13 Thread 0x00000000 0000000e got a signal delivered to it

14 Main completed

我们可以通过截获的signal信号,来释放掉线程申请的资源,可是遗憾的是我们不能再signal处理里调用pthread_exit来终结掉线程,因为pthread_exit是中介当前线程,而signal被调用的方式可以理解为内核的回调,不是在同一个线程运行的,所以这里只能做处理释放资源的事情,线程内部只有判断有没有被中断(一般是EINTR)来断定是否要求自己结束,判定后可以调用pthread_exit退出。

此法对于一般的操作也是非常可行的,可是在有的情况下就不是一个比较好的方法了,比如我们有一些线程在处理网络IO事件,假设它是一种一个客户端对应一个服务器线程,阻塞从Socket中读消息的情况。我们一般在网络IO的库里面回家上对EINTR信号的处理,例如recv时发现返回值小于0,检查error后,会进行他对应的操作。有可能他会再recv一次,那就相当于我的线程根本就不回终止,因为网络IO的类有可能不知道在获取EINTR时要终止线程。也就是说这不是一个特别好的可移植方案,如果你线程里的操作使用了很多外来的不太熟悉的类,而且你并不是他对EINTR的处理手段是什么,这是你在使用这样的方法来终止就有可能出问题了。而且如果你不是特别熟悉这方面的话你会很苦恼,“为什么我的测试代码全是ok的,一加入你们部门开发的框架进来就不ok了,肯定是你们框架出问题了”。好了,为了不必要的麻烦,我最后没有使用这个方案。

pthread_cancel

这个方案是我最终采用的方案,我认为是解决这个问题,通用的最好的解决方案,虽然前面其他方案的有些问题他可能也不好解决,但是相比较而言,还是相当不错的

pthread_cancel可以单独使用,因为在很多系统函数里面本身就有很多的断点,当调用这些系统函数时就会命中其内部的断点来结束线程,如下面的代码中,即便注释掉我们自己设置的断点pthread_testcancel()程序还是一样的会被成功的cancel掉,因为printf函数内部有取消点(如果大家想了解更多的函数的取消点情况,可以阅读《Unix高级环境编程》的线程部分)

1 #include pthread.h

2 #include stdio.h

3 #includestdlib.h

4 #include unistd.h

5 void *threadfunc(void *parm)

6 {

7 printf("Entered secondary thread\n");

8 while (1) {

9 printf("Secondary thread is looping\n");

10 pthread_testcancel();

11 sleep(1);

12 }

13 return NULL;

14 }

15

16 int main(int argc, char **argv)

17 {

18 pthread_t thread;

19 int rc=0;

20

21 printf("Entering testcase\n");

22

23 /* Create a thread using default attributes */

24 printf("Create thread using the NULL attributes\n");

25 rc = pthread_create(thread, NULL, threadfunc, NULL);

26 checkResults("pthread_create(NULL)\n", rc);

27

28 /* sleep() is not a very robust way to wait for the thread */

29 sleep(1);

30

31 printf("Cancel the thread\n");

32 rc = pthread_cancel(thread);

33 checkResults("pthread_cancel()\n", rc);

34

35 /* sleep() is not a very robust way to wait for the thread */

36 sleep(10);

37 printf("Main completed\n");

38 return 0;

39 }

输出:

Entering testcase

Create thread using the NULL attributes

Entered secondary thread

Secondary thread is looping

Cancel the thread

Main completed

POSIX保证了绝大部分的系统调用函数内部有取消点,我们看到很多在cancel调用的情景下,recv和send函数最后都会设置pthread_testcancel()取消点,其实这不是那么有必要的,那么究竟什么时候该pthread_testcancel()出场呢?《Unix高级环境编程》也说了,当遇到大量的基础计算时(如科学计算),需要自己来设置取消点。

ok,得益于pthread_cancel,我们很轻松的把线程可以cancel掉,可是我们的资源呢?何时释放...

下面来看两个pthread函数

1.void pthread_cleanup_push(void (*routine)(void *), void *arg);

2.void pthread_cleanup_pop(int execute);

这两个函数能够保证在 1函数调用之后,2函数调用之前的任何形式的线程结束调用向pthread_cleanup_push注册的回调函数

另外我们还可通过下面这个函数来设置一些状态。

为什么pthread_kill发送一个alarm信号给线程,会导致进程退出?

你有没有预置收到alarm信号的执行函数?如果没有的话,收到SIGALRM信号的默认操作是结束当前进程,线程有是在一个进程中的,所以当前进程结束了,你的进程就退出了

在什么情况下pthread_kill(threadid,0) 返回 EINVAL

#include stdio.h

#include stdlib.h

#include pthread.h

#include errno.h

void *func1()

{

sleep(1);

printf("线程1(ID:0x%x)退出。\n",(unsigned int)pthread_self());

pthread_exit((void *)0);

}

void *func2()

{

sleep(5);

printf("线程2(ID:0x%x)退出。\n",(unsigned int)pthread_self());

pthread_exit((void *)0);

}

void test_pthread(pthread_t tid)

{

int pthread_kill_err;

pthread_kill_err = pthread_kill(tid,0);

if(pthread_kill_err == ESRCH)

printf("ID为0x%x的线程不存在或者已经退出。\n",(unsigned int)tid);

else if(pthread_kill_err == EINVAL)

printf("发送信号非法。\n");

else

printf("ID为0x%x的线程目前仍然存活。\n",(unsigned int)tid);

}

int main()

{

int ret;

pthread_t tid1,tid2;

pthread_create(tid1,NULL,func1,NULL);

pthread_create(tid2,NULL,func2,NULL);

sleep(3);

test_pthread(tid1);

test_pthread(tid2);

exit(0);

}

iOS详解多线程(实现篇——pThread)

上一节中,我们探究了OC中重要的实现多线程的方法——NSOperation。本节中,我们了解一下不常用的一种创建多线程的方式——pThread。

相关链接:

NSOpreation链接: iOS详解多线程(实现篇——NSOperation)

GCD链接: iOS详解多线程(实现篇——GCD)

NSThread链接: 详解多线程(实现篇——NSThread)

多线程概念篇链接: 详解多线程(概念篇——进程、线程以及多线程原理)

源码链接:

1.NSThread(OC)

2.GCD(C语言)

3.NSOperation(OC)

5.其他实现多线程方法

pThread并不是OC特有的实现多线程的方法,而是Unix、Linux还有Windows都通用的一种实现多线程的方式。

pThread的全称是POSIX threads,是线程的 POSIX 标准。

pThread是C语言的,在iOS的开发中极少使用。

使用之前,记得先导入头文件

运行结果:

从结果可以看出,开启了新的线程,执行任务。

pthread_create() 创建一个线程

pthread_exit() 终止当前线程

pthread_cancel() 中断另外一个线程的运行

pthread_join() 阻塞当前的线程,直到另外一个线程运行结束

pthread_attr_init() 初始化线程的属性

pthread_attr_setdetachstate() 设置脱离状态的属性(决定这个线程在终止时是否可以被结合)

pthread_attr_getdetachstate() 获取脱离状态的属性

pthread_attr_destroy() 删除线程的属性

pthread_kill() 向线程发送一个信号

由于pThread我们基本用不到,所以不再做深入研究。

(责任编辑:IT教学网)

更多

推荐淘宝营销文章