如何取消子POSIX线程

6
// threadA.c
int main() {
    int res;
    pthread_t a_thread;
    void *thread_result;

    res = pthread_create(&a_thread, NULL, thread_function, NULL);
    if (res != 0) {
        perror("Thread creation failed");
        exit(EXIT_FAILURE);
    }
    sleep(3);
    printf("Canceling thread...\n");
    res = pthread_cancel(a_thread);
    if (res != 0) {
        perror("Thread cancelation failed");
        exit(EXIT_FAILURE);
    }
    printf("Waiting for thread to finish...\n");
    res = pthread_join(a_thread, &thread_result);
    if (res != 0) {
        perror("Thread join failed");
        exit(EXIT_FAILURE);
    }
    exit(EXIT_SUCCESS);
}

void *thread_function(void *arg) {
    int i, res, j;
    res = pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
    if (res != 0) {
        perror("Thread pthread_setcancelstate failed");
        exit(EXIT_FAILURE);
    }
    res = pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL);
    if (res != 0) {
        perror("Thread pthread_setcanceltype failed");
        exit(EXIT_FAILURE);
    }
    printf("thread_function is running\n");
    for(i = 0; i < 10; i++) {
        printf("Thread is still running (%d)...\n", i);
        sleep(1);
    }
    pthread_exit(0);
}

输出结果如下:

$ ./threadA
thread_function is running
Thread is still running (0)...
Thread is still running (1)...
Thread is still running (2)...
Canceling thread...
Waiting for thread to finish...
$

在等待3秒钟后,主线程发出pthread_cancel命令来停止子线程,在调用pthread_join命令后,子线程确实开始响应取消。

此时,主线程运行到pthread_join之后的下一行代码,而子线程则正在运行以下代码的循环内部:

    for(i = 0; i < 10; i++) {
        printf("Thread is still running (%d)...\n", i);
        sleep(1);
    }

我在这个循环中没有看到任何检查语句,但主线程仍然能够取消子线程。我假设POSIX多线程系统内部有一个检查系统,以便在主线程调用pthread_join时终止子线程。

问题>

基本上,我需要了解如何在循环中取消子线程而不检查任何标志。

如果有任何错误,请纠正我的描述。

2个回答

7
发生的情况是,您的循环包含至少一个取消点,sleep(可能还有两个,因为printf是可选的取消点)。

成为取消点意味着该函数包含类似于以下逻辑:

if (thread_local_cancellation_flag) {
    pthread_setcancelstate(PTHREAD_CANCEL_DISABLE);
    pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED);
    pthread_exit(PTHREAD_CANCELED);
}

实际上,情况要复杂一些,因为如果取消请求在函数处于“等待”或“阻塞”状态时到达(例如,等待睡眠时间到期或来自套接字的输入),则必须对其进行处理。因此,需要某种异步传递机制,典型的实现方式是使用信号,但实际上很难做到完美,并且流行的实现效果并不好。有关glibc中一些丑陋的边缘情况,其他实现可能也存在,请参见此错误报告:http://sourceware.org/bugzilla/show_bug.cgi?id=12683 在您的情况下,几乎肯定发生的是,在线程处于sleep状态时,取消请求(通过信号)到达,并且信号处理程序运行,确定它正在进行可取消操作,并对取消请求进行操作。

3
请先阅读pthread_cancel手册页面。它解释了很多内容。特别是针对您的问题,在循环内部不应有检查语句。我没有检查过Linux实现,但合理的做法是向线程发送信号请求线程停止/取消。如果在信号处理程序中确定线程处于不可取消状态,则请求将排队。一旦调用任何一个取消点函数并确定新状态为可取消状态,则会检查该队列,并且如果在队列中找到取消请求,则线程将被取消。基本上,不可取消状态是临界区。在您的情况下,当调用sleep()时,所有线程都会被取消,因为那是一个取消点。请参见pthreads (7)

pthread_setcanceltypepthread_setcancelstate不是取消点。除非取消类型为异步,否则仅在取消点处检查取消状态。在这种情况下,它可能会在其他点被检查并异步中断代码(但不是必需的)。 - R.. GitHub STOP HELPING ICE

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接