pthread_exit() 和 pthread_cancel() 如何调用 Linux 中的清理例程?
How pthread_exit() and pthread_cancel() calls cleanup routine in Linux?
我在学习有关 pthread_cleanup_push 的概念时观察到,pthread_exit() 和 pthread_cancel() 以不同的方式影响 pthread_cleanup_pop()。下面是代码示例。
void push_routine_1(void * arg) {
printf(" Push Routine 1\n");
}
void push_routine_2(void * arg) {
printf(" Push Routine 2\n");
}
void push_routine_3(void * arg) {
printf(" Push Routine 3\n");
}
void push_routine_4(void * arg) {
printf(" Push Routine 4\n");
}
void * thread_routine(void * arg) {
pthread_cleanup_push(push_routine_1, NULL);
pthread_cleanup_push(push_routine_2, NULL);
pthread_cleanup_push(push_routine_3, NULL);
pthread_cleanup_push(push_routine_4, NULL);
pthread_exit(NULL);
pthread_cleanup_pop(1);
pthread_cleanup_pop(1);
pthread_cleanup_pop(1);
pthread_cleanup_pop(1);
}
以上程序给出了这样的输出:
Push Routine 4
Push Routine 3
Push Routine 2
Push Routine 1
当使用 pthread_cancel(pthread_self())
而不是 pthread_exit(NULL)
时,输出如下
Push Routine 3
Push Routine 2
Push Routine 1
使用 pthread_cancel()
时不执行 push_routine_4()
的原因是什么?
我认为您不会认为 pthread_cancel(pthread_self())
与 pthread_exit(NULL)
完全相同。
POSIX 没有告诉我期望 pthread_cancel(pthread_self())
做什么。但我注意到它说:
2.9.5 Thread Cancellation
The thread cancellation mechanism allows a thread to terminate the execution of any other thread in the process in a controlled manner.
其中“任何 其他 线程”确实让我感到奇怪。但我不认为这是问题所在。
默认 'cancelability type' 为 PTHREAD_CANCEL_DEFERRED。因此,pthread_cancel()
直到取消线程中的下一个 'Cancellation Point' 才会生效。因此,您的代码将继续到第一个 pthread_cleanup_pop(1)
,即 而不是 'Cancellation Point',然后 printf(" Push Routine 4\n")
,可能是一个'Cancellation Point'.
我调整了你的代码如下:
static volatile unsigned step ;
void push_routine_1(void * arg) {
step += 10 ;
printf(" Push Routine 1 -- %u\n", step);
step += 10 ;
}
void push_routine_2(void * arg) {
step += 100 ;
printf(" Push Routine 2 -- %u\n", step);
step += 100 ;
}
void push_routine_3(void * arg) {
step += 1000 ;
printf(" Push Routine 3 -- %u\n", step);
step += 1000 ;
}
void push_routine_4(void * arg) {
step += 10000 ;
printf(" Push Routine 4 -- %u\n", step);
step += 10000 ;
}
void * thread_routine(void * arg)
{
unsigned frag = *(unsigned*)arg ;
printf("Start %u\n", frag) ;
step = 0 ;
pthread_cleanup_push(push_routine_1, NULL); step += 10 ;
pthread_cleanup_push(push_routine_2, NULL); step += 100;
pthread_cleanup_push(push_routine_3, NULL); step += 1000 ;
pthread_cleanup_push(push_routine_4, NULL); step += 10000 ;
step++ ;
if (frag == 0)
pthread_exit(NULL) ; // 11111
else
pthread_cancel(pthread_self()) ;
step++ ; // 11112
if (frag == 2)
printf(" Continue %u\n", step) ;
step++ ; // 11113
pthread_cleanup_pop(1); // (4)
step++ ; // xxxx4
pthread_cleanup_pop(1); // (3)
step++ ; // xxxx5
pthread_cleanup_pop(1); // (2)
step++ ; // xxxx6
pthread_cleanup_pop(1); // (1)
step++ ; // xxxx7
}
int
main(Unused int argc, Unused char* argv[])
{
unsigned frag ;
for (frag = 0 ; frag < 3 ; ++frag)
{
pthread_t thr ;
pthread_create(&thr, NULL, thread_routine, &frag) ;
pthread_join(thr, NULL) ;
printf(" Finally %u\n", step) ;
} ;
return 0 ;
}
结果是:
Start 0
Push Routine 4 -- 21111
Push Routine 3 -- 32111
Push Routine 2 -- 33211
Push Routine 1 -- 33321
Finally 33331
Start 1
Push Routine 4 -- 21113
Push Routine 3 -- 22113
Push Routine 2 -- 23213
Push Routine 1 -- 23323
Finally 23333
Start 2
Continue 11112
Push Routine 4 -- 21112
Push Routine 3 -- 32112
Push Routine 2 -- 33212
Push Routine 1 -- 33322
Finally 33332
因此:
'Start 0' 我们看到(正如预期的那样)线程不会继续进行 pthread_exit(NULL)
.
'Start 1' 我们看到线程到达 pthread_cleanup_pop(1); // (4)
,但是 push_routine_4()
没有 通过 printf()
.
我相信这表明一旦 printf()
完成,取消过程就会开始。
'Start 2' 我们看到 push_routine_4()
在 printf(" Continue %u\n", step)
之后和紧随其后的 step++
之前被调用。
我相信这表明一旦 printf(" Continue ...)
完成,取消过程就会开始。
在我的机器上,Linux 5.6.8 运行 glibc 2.30,函数完成后,printf()
似乎是一个 'Cancellation Point'。您得到的效果表明 printf()
在打印任何内容之前可能是 'Cancellation Point' ? [或者 push_routine_4()
中的 printf()
没有产生任何输出的其他原因。]
我在学习有关 pthread_cleanup_push 的概念时观察到,pthread_exit() 和 pthread_cancel() 以不同的方式影响 pthread_cleanup_pop()。下面是代码示例。
void push_routine_1(void * arg) {
printf(" Push Routine 1\n");
}
void push_routine_2(void * arg) {
printf(" Push Routine 2\n");
}
void push_routine_3(void * arg) {
printf(" Push Routine 3\n");
}
void push_routine_4(void * arg) {
printf(" Push Routine 4\n");
}
void * thread_routine(void * arg) {
pthread_cleanup_push(push_routine_1, NULL);
pthread_cleanup_push(push_routine_2, NULL);
pthread_cleanup_push(push_routine_3, NULL);
pthread_cleanup_push(push_routine_4, NULL);
pthread_exit(NULL);
pthread_cleanup_pop(1);
pthread_cleanup_pop(1);
pthread_cleanup_pop(1);
pthread_cleanup_pop(1);
}
以上程序给出了这样的输出:
Push Routine 4
Push Routine 3
Push Routine 2
Push Routine 1
当使用 pthread_cancel(pthread_self())
而不是 pthread_exit(NULL)
时,输出如下
Push Routine 3
Push Routine 2
Push Routine 1
使用 pthread_cancel()
时不执行 push_routine_4()
的原因是什么?
我认为您不会认为 pthread_cancel(pthread_self())
与 pthread_exit(NULL)
完全相同。
POSIX 没有告诉我期望 pthread_cancel(pthread_self())
做什么。但我注意到它说:
2.9.5 Thread Cancellation
The thread cancellation mechanism allows a thread to terminate the execution of any other thread in the process in a controlled manner.
其中“任何 其他 线程”确实让我感到奇怪。但我不认为这是问题所在。
默认 'cancelability type' 为 PTHREAD_CANCEL_DEFERRED。因此,pthread_cancel()
直到取消线程中的下一个 'Cancellation Point' 才会生效。因此,您的代码将继续到第一个 pthread_cleanup_pop(1)
,即 而不是 'Cancellation Point',然后 printf(" Push Routine 4\n")
,可能是一个'Cancellation Point'.
我调整了你的代码如下:
static volatile unsigned step ;
void push_routine_1(void * arg) {
step += 10 ;
printf(" Push Routine 1 -- %u\n", step);
step += 10 ;
}
void push_routine_2(void * arg) {
step += 100 ;
printf(" Push Routine 2 -- %u\n", step);
step += 100 ;
}
void push_routine_3(void * arg) {
step += 1000 ;
printf(" Push Routine 3 -- %u\n", step);
step += 1000 ;
}
void push_routine_4(void * arg) {
step += 10000 ;
printf(" Push Routine 4 -- %u\n", step);
step += 10000 ;
}
void * thread_routine(void * arg)
{
unsigned frag = *(unsigned*)arg ;
printf("Start %u\n", frag) ;
step = 0 ;
pthread_cleanup_push(push_routine_1, NULL); step += 10 ;
pthread_cleanup_push(push_routine_2, NULL); step += 100;
pthread_cleanup_push(push_routine_3, NULL); step += 1000 ;
pthread_cleanup_push(push_routine_4, NULL); step += 10000 ;
step++ ;
if (frag == 0)
pthread_exit(NULL) ; // 11111
else
pthread_cancel(pthread_self()) ;
step++ ; // 11112
if (frag == 2)
printf(" Continue %u\n", step) ;
step++ ; // 11113
pthread_cleanup_pop(1); // (4)
step++ ; // xxxx4
pthread_cleanup_pop(1); // (3)
step++ ; // xxxx5
pthread_cleanup_pop(1); // (2)
step++ ; // xxxx6
pthread_cleanup_pop(1); // (1)
step++ ; // xxxx7
}
int
main(Unused int argc, Unused char* argv[])
{
unsigned frag ;
for (frag = 0 ; frag < 3 ; ++frag)
{
pthread_t thr ;
pthread_create(&thr, NULL, thread_routine, &frag) ;
pthread_join(thr, NULL) ;
printf(" Finally %u\n", step) ;
} ;
return 0 ;
}
结果是:
Start 0
Push Routine 4 -- 21111
Push Routine 3 -- 32111
Push Routine 2 -- 33211
Push Routine 1 -- 33321
Finally 33331
Start 1
Push Routine 4 -- 21113
Push Routine 3 -- 22113
Push Routine 2 -- 23213
Push Routine 1 -- 23323
Finally 23333
Start 2
Continue 11112
Push Routine 4 -- 21112
Push Routine 3 -- 32112
Push Routine 2 -- 33212
Push Routine 1 -- 33322
Finally 33332
因此:
'Start 0' 我们看到(正如预期的那样)线程不会继续进行
pthread_exit(NULL)
.'Start 1' 我们看到线程到达
pthread_cleanup_pop(1); // (4)
,但是push_routine_4()
没有 通过printf()
.我相信这表明一旦
printf()
完成,取消过程就会开始。'Start 2' 我们看到
push_routine_4()
在printf(" Continue %u\n", step)
之后和紧随其后的step++
之前被调用。我相信这表明一旦
printf(" Continue ...)
完成,取消过程就会开始。
在我的机器上,Linux 5.6.8 运行 glibc 2.30,函数完成后,printf()
似乎是一个 'Cancellation Point'。您得到的效果表明 printf()
在打印任何内容之前可能是 'Cancellation Point' ? [或者 push_routine_4()
中的 printf()
没有产生任何输出的其他原因。]