Bootstrap

Linux-----线程操作(终止)

 目录

 exit&join

pthread_exit

pthread_join

 案例(使用pthread_join接受线程接收返回的结果)

pthread_detach

​​​​​​​pthread_cancel


 exit&join

pthread_exit

线程终止有以下几种方法:

  • 线程函数执行return语句;
  • 线程函数内部调用pthread_exit函数;
  • 其他线程调用pthread_cancel函数。
#include <pthread.h>

/**
 * 结束关闭调用该方法的线程,并返回一个内存指针用于存放结果
 * void *retval: 要返回给其它线程的数据
 */
void pthread_exit(void *retval);

当某个线程调用pthread_exit方法后,该线程会被关闭(相当于return)。线程可以通过retval向其它线程传递信息,retval指向的区域不可以放在线程函数的栈内。其他线程(例如主线程)如果需要获得这个返回值,需要调用pthread_join方法。

pthread_join

#include <pthread.h>

/**
 * 等待指定线程结束,获取目标线程的返回值,并在目标线程结束后回收它的资源
 * 
 * pthread_t thread: 指定线程ID
 * void **retval: 这是一个可选参数,用于接收线程结束后传递的返回值。如果非空,pthread_join 会在成功时将线程的 exit status 复制到 *retval 所指向的内存位置。如果线程没有显式地通过 pthread_exit 提供返回值,则该参数将被设为 NULL 或忽略
 * return: int 成功 0
 *              失败 1
 */
int pthread_join(pthread_t thread, void **retval);

 案例(使用pthread_join接受线程接收返回的结果

#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <math.h>
#include <string.h> 

typedef struct Result {
    char* p;
    int len;
}Result;

void* red_thread(void* arg) {
    Result* result = (Result*)malloc(sizeof(Result));
    // 解析
    char code = *((char*)arg);
    // 读取缓冲区
    char* ans = (char*)malloc(sizeof(char) * 100);
    while (1)
    {
        fgets(ans, 100, stdin);
        // 是我的
        if (ans[0] == code) {
            // 接受到了回复的消息了
            free(ans);
            printf("red线程结束\n");
            char* redans = strdup("红色风暴走了\n");
            result->p = redans;
            result->len = strlen(redans);
            // 结束线程
            pthread_exit((void*)result);
        }
        // 不是我的
        else {
            printf("红色风暴在等你\n");
        }
    }
}

void* blue_thread(void* arg) {
     Result* result = (Result*)malloc(sizeof(Result));
    // 解析
    char code = *((char*)arg);
    // 读取缓冲区
    char* ans = (char*)malloc(sizeof(char) * 100);
    while (1)
    {
        fgets(ans, 100, stdin);
        // 是我的
        if (ans[0] == code) {
            // 接受到了回复的消息了
            free(ans);
            printf("blue线程结束\n");
            char* redans = strdup("蓝色妖姬走了\n");
            result->p = redans;
            result->len = strlen(redans);
            // 结束线程
            pthread_exit((void*)result);
        }
        // 不是我的
        else {
            printf("蓝色妖姬在等你\n");
        }
    }
}

int main(int argc, char const *argv[])
{
    pthread_t pid_red,pid_blue;
    char red = 'r';
    char blue = 'b';
    Result* red_result = NULL;
    Result* blue_result = NULL;


    // 创建线程
    pthread_create(&pid_red, NULL, red_thread, &red);
    pthread_create(&pid_blue, NULL, blue_thread, &blue);

    // 获取线程结果
    pthread_join(pid_red, (void**)&red_result);
    printf("红色风暴:%s\n", red_result->p);
    pthread_join(pid_blue, (void**)&blue_result);
    printf("蓝色妖姬:%s\n", blue_result->p);

    // 释放资源
    free(red_result->p);
    free(red_result);
    free(blue_result->p);
    free(blue_result);
    return 0;
}

这里会出现资源竞争的情况,控制台输入一次只能被其中一个线程响应 

​​​​​​​pthread_detach

这个方式就是使用异步自动回收,主线程不需要等待子线程就会自动回收,跟join不同,这里没有同步而是异步。 

#include <pthread.h>

/**
 * @brief 将线程标记为detached状态。POSIX线程终止后,如果没有调用pthread_detach或pthread_join,其资源会继续占用内存,类似于僵尸进程的未回收状态。默认情况下创建线程后,它处于可join状态,此时可以调用pthread_join等待线程终止并回收资源。但是如果主线程不需要等待线程终止,可以将其标记为detached状态,这意味着线程终止后,其资源会自动被系统回收。
 * 
 * @param thread 线程ID
 * @return int 成功返回0,失败返回错误码
 */
int pthread_detach(pthread_t thread);

​​​​​​​pthread_cancel

#include <pthread.h>

/**
 * @brief 向目标线程发送取消请求。目标线程是否和何时响应取决于它的取消状态和类型
 *  取消状态(Cancelability State):可以是enabled(默认)或disabled。如果取消状态为禁用,则取消请求会被挂起,直至线程启用取消功能。如果取消状态为启用,则线程的取消类型决定它何时取消。
 *  取消类型(Cancelability Type):可以是asynchronous(异步)或deferred(被推迟,默认值)。
 *      asynchronous:意味着线程可能在任何时候被取消(通常立即被取消,但系统并不保证这一点)
 *      deferred:被推迟意味着取消请求会被挂起,直至被取消的线程执行取消点(cancellation point)函数时才会真正执行线程的取消操作。
 *      取消点函数:是在POSIX线程库中专门设计用于检查和处理取消请求的函数。当被取消的线程执行这些函数时,如果线程的取消状态是enabled且类型是deferred,则它会立即响应取消请求并终止执行。man 7 pthreads可以看到取消点函数列表。
 * 
 * @param thread 目标线程,即被取消的线程
 * @return int 成功返回0,失败返回非零的错误码
 *      需要注意的是,取消操作和pthread_cancel函数的调用是异步的,这个函数的返回值只能告诉调用者取消请求是否成功发送。当线程被成功取消后,通过pthread_join和线程关联将会获得PTHREAD_CANCELED作为返回信息,这是判断取消是否完成的唯一方式
 */
int pthread_cancel(pthread_t thread);
#include <stdlib.h>
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>

void* fun(void* arg) {
    printf("线程启动\n");
    printf("working\n");
    sleep(1);
    pthread_testcancel(); // 取消点函数
    printf("线程结束\n");
    return NULL;
}

int main(int argc, char const* argv[])
{
    // 创建线程
    pthread_t tid;
    pthread_create(&tid, NULL, fun, NULL);

    // 取消子线程
    if (pthread_cancel(tid) != 0) {
        perror("pthread_cancel");
    }

    // pthread_cancel只是发出停止的命令,不会返回结果的
    void* res;
    pthread_join(tid, &res);
    if (res == PTHREAD_CANCELED) {
        printf("线程被取消了\n");
    }
    else {
        printf("线程没有取消 code: %ld\n", (long)res);
    }
    return 0;
}

;