pthread_cancel函数注意事项

本文详细探讨了在Linux环境下使用pthread_cancel函数终止线程时的关键点,包括取消点、清理例程、线程状态转换等核心概念。通过实例解析,阐述了如何正确和安全地在多线程程序中应用pthread_cancel,以及避免潜在的问题和陷阱。
/**************************************************
相关函数:
     #include <pthread.h>
     int pthread_cancel(pthread_t thread)
                     成功返回0,失败返回错误码
**************************************************/

此函数是POSIX提供的用于取消同一进程中的其它线程,此函
数只发送取消请求,并不等待要取消的进程退出!线程可以
选择忽略或是选择其它动作!

需要注意的是:
当我们调用它取消一个已经获得互斥锁/匿名信号量/写锁....但
还未释放其所获得锁的线程时,如果此线程被取消,此后所有想
要获得此时执行任务的线程都将处于睡眠状态,直到此锁被释放.
为避免此问题的产生,我们可以调用一组函数:
/**************************************************
    #include <pthread.h>

    void pthread_cleanup_push(void (*routine)(void *), void *arg)
    void pthread_cleanup_pop(int execute)
        参数解释:routine为函数指针,arg为传递给routine的参数
        execute为零时,从栈中删除注册的函数,删除后将再也
        不被执行。
**************************************************/
这两个函数被称为线程清理处理程序,类似于atexit函数,我们
可以注册多个清理函数,当执行以下动作时我们所注册的函数将会
回调(执行顺序与注册顺序相反):
    1.线程从pthread_exit(void *)函数退出时。
    2.线程响应取消请求时。
    3.执行pthread_cleanup_pop函数,execute参数为非零时。

这两个线程清理处理程序必须成对出现,必须处于同一作用域中,
否则会编译出错。
实例:
    如何使用这些函数处理以上问题!
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <unistd.h>
#include <signal.h>

#include <sys/stat.h>
#include <sys/types.h>
#include <errno.h>

#include <pthread.h>

pthread_mutex_t m = PTHREAD_MUTEX_INITIALIZER;

void *count(void *arg)
{
    int i=1;
    while(1)
    {
        sleep(1);
        printf("sec: %d\n", i++);
    }
}

void handler(void *arg)
{
    printf("[%u] is cancelled.\n", (unsigned)pthread_self());
    pthread_mutex_t *pm = (pthread_mutex_t *)arg;

    pthread_mutex_unlock(pm);
}

void *routine(void *arg)
{
    #ifdef CLEANUP
    pthread_cleanup_push(handler, (void *)&m);
    #endif

    pthread_mutex_lock(&m);
    printf("[%u] lock the mutex!\n", (unsigned)pthread_self());

    /*
    ** During sleep(), if the calling thread received a cancel-
    ** request and HASN'T established any cleanup handlers to
    ** unlock the mutex, it will leave the mutex a DEAD-LOCK
    ** state.
    */
    sleep(2);
    printf("[%u]: job finished!\n", (unsigned)pthread_self());


    pthread_mutex_unlock(&m);
    printf("[%u] unlock the mutex!\n", (unsigned)pthread_self());

    /*
    ** NOTE: 
    **
    ** pthread_cleanup_push() and pthread_cleanup_pop() may be
    ** implemented as macro that expand to text containing '{'
    ** and '}', respectively. For this reason, the caller must
    ** user them pairly and ensure that they are paired within
    ** a same function and at the same lexical nesting level.
    */
    #ifdef CLEANUP
    pthread_cleanup_pop(0);
    #endif


    pthread_exit(NULL);
}

int main(int argc, char **argv)
{
    pthread_t t, t1, t2;
    pthread_create(&t, NULL, count, NULL);


    pthread_create(&t1, NULL, routine, NULL);
    pthread_create(&t2, NULL, routine, NULL);
    printf("[%u] ==> t1\n", (unsigned)t1);
    printf("[%u] ==> t2\n", (unsigned)t2);
    printf("[%u] ==> main\n", (unsigned)pthread_self());

    sleep(1);
    pthread_cancel(t1);
    pthread_cancel(t2);

    sleep(2);

    pthread_mutex_lock(&m);
    printf("[%u] locked the mutex!\n",
        (unsigned)pthread_self());
    pthread_mutex_unlock(&m);

    exit(0);
}
### `pthread_setcancelstate` 函数使用 `PTHREAD_CANCEL_ENABLE` 参数的详细解析 #### 函数原型 `pthread_setcancelstate` 函数的原型如下: ```c #include <pthread.h> int pthread_setcancelstate(int state, int *oldstate); ``` #### 使用 `PTHREAD_CANCEL_ENABLE` 参数的使用方法 当调用 `pthread_setcancelstate` 函数时,传入 `PTHREAD_CANCEL_ENABLE` 作为 `state` 参数,`oldstate` 参数如果不为 `NULL`,则会存储线程原来的取消状态,以便后续恢复。示例代码如下: ```c #include <pthread.h> #include <stdio.h> #include <unistd.h> void* thread_function(void* arg) { int oldstate; // 设置线程可取消 if (pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &oldstate) != 0) { perror("pthread_setcancelstate"); return NULL; } printf("Thread is now cancelable. Old state was %s\n", oldstate == PTHREAD_CANCEL_ENABLE ? "PTHREAD_CANCEL_ENABLE" : "PTHREAD_CANCEL_DISABLE"); // 模拟线程工作 while (1) { sleep(1); } return NULL; } int main() { pthread_t thread; if (pthread_create(&thread, NULL, thread_function, NULL) != 0) { perror("pthread_create"); return 1; } // 主线程等待一段时间后取消子线程 sleep(3); if (pthread_cancel(thread) != 0) { perror("pthread_cancel"); return 1; } // 等待子线程结束 void* result; if (pthread_join(thread, &result) != 0) { perror("pthread_join"); return 1; } if (result == PTHREAD_CANCELED) { printf("Thread was canceled successfully.\n"); } return 0; } ``` #### 作用 `PTHREAD_CANCEL_ENABLE` 用于将线程设置为可取消状态。当线程处于可取消状态时,它可以响应 `pthread_cancel` 函数发送的取消请求。当其他线程调用 `pthread_cancel` 向该线程发送取消请求时,该线程会在合适的取消点进入取消处理流程,最终终止执行 [^2]。 #### 注意事项 - **取消点**:线程只有在到达取消点时才会真正响应取消请求。取消点是线程执行过程中的一些特定位置,例如调用某些系统调用(如 `read`、`write`、`sleep` 等)。如果线程在没有到达取消点的情况下收到取消请求,它会暂时忽略该请求,直到到达下一个取消点 [^1]。 - **资源清理**:线程被取消时,需要确保已经分配的资源(如内存、文件描述符等)被正确释放。可以使用 `pthread_cleanup_push` 和 `pthread_cleanup_pop` 函数来注册和执行清理处理程序。 - **线程安全**:在多线程环境中,多个线程可能同时调用 `pthread_setcancelstate` 函数,因此需要确保线程安全。可以使用互斥锁等同步机制来保护对线程取消状态的修改。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值