pthreadcreate创建线程后,如何正确处理其返回值?

99ANYc3cd6
预计阅读时长 18 分钟
位置: 首页 C语言 正文

pthread_create 是 POSIX 线程(Pthreads)库中用于创建新线程的核心函数,它定义在 <pthread.h> 头文件中。

c语言 pthreadcreate
(图片来源网络,侵删)

函数原型

#include <pthread.h>
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
                   void *(*start_routine)(void *), void *arg);

参数详解

这个函数接受四个参数,我们来逐一分解:

参数 1: pthread_t *thread

  • 类型: 指向 pthread_t 对象的指针。
  • 作用: 这是一个输出参数,当 pthread_create 成功创建一个新线程后,系统会为新线程生成一个唯一的 ID,并将这个 ID 存储在你传入的 pthread_t 变量中。
  • 后续用途: 你可以使用这个线程 ID 来对线程进行其他操作,比如等待线程结束(pthread_join)、分离线程(pthread_detach)等。
  • 示例:
    pthread_t my_thread_id;
    pthread_create(&my_thread_id, NULL, my_function, NULL);

参数 2: const pthread_attr_t *attr

  • 类型: 指向 pthread_attr_t 结构体的常量指针。
  • 作用: 用于指定新线程的属性,线程的栈大小、调度策略、是否分离等。
  • 常用值:
    • NULL: 这是最常用的值,它表示使用线程的默认属性,对于绝大多数简单应用来说,默认属性就足够了。
  • 自定义属性: 如果你需要设置特定属性,你需要先初始化一个属性对象,然后使用 pthread_attr_init, pthread_attr_setstacksize 等函数来修改它,最后将这个对象的地址传给 pthread_create
  • 示例:
    pthread_create(&my_thread_id, NULL, my_function, NULL); // 使用默认属性

参数 3: void *(*start_routine)(void *)

  • 类型: 一个函数指针。

  • 作用: 这是新线程一旦被创建就会立即执行的函数的地址,这个函数也被称为线程的入口函数启动例程

  • 函数签名要求:

    c语言 pthreadcreate
    (图片来源网络,侵删)
    • 它必须接受一个 void * 类型的参数。
    • 它必须返回一个 void * 类型的值。
  • 示例:

    // 线程将要执行的函数
    void* my_function(void* arg) {
        // 线程的具体逻辑...
        return NULL; // 必须返回一个 void* 类型的值
    }
    // 在主线程中调用
    pthread_create(&my_thread_id, NULL, my_function, NULL);

参数 4: void *arg

  • 类型: 指向 void 的指针 (void *)。

  • 作用: 这是传递给线程入口函数(start_routine)的参数

  • 如何传递多个参数? void * 只能传递一个指针,如果你想传递多个参数,你需要将它们打包到一个结构体中,然后将这个结构体的地址作为 arg 传递,在线程函数内部,再将这个指针强制转换回结构体指针,然后从中取出各个成员。

    c语言 pthreadcreate
    (图片来源网络,侵删)
  • NULL: 如果你不需要给线程函数传递任何参数,可以直接传入 NULL

  • 示例:

    // 定义一个结构体来打包多个参数
    struct my_args {
        int id;
        char *message;
    };
    void* my_function(void* args) {
        struct my_args *real_args = (struct my_args *)args;
        printf("Thread ID: %d, Message: %s\n", real_args->id, real_args->message);
        free(real_args); // 记得释放动态分配的内存!
        return NULL;
    }
    int main() {
        pthread_t my_thread;
        struct my_args *args = malloc(sizeof(struct my_args));
        args->id = 1;
        args->message = "Hello from thread!";
        pthread_create(&my_thread, NULL, my_function, (void*)args);
        // ... 其他代码 ...
        pthread_join(my_thread, NULL); // 等待线程结束
        return 0;
    }

返回值

  • 成功: 返回 0
  • 失败: 返回一个非零的错误码,你可以使用 perror 函数或 strerror 函数来打印出具体的错误信息。
  • 示例:
    if (pthread_create(&my_thread, NULL, my_function, NULL) != 0) {
        perror("Failed to create thread");
        return 1; // 返回错误码
    }

一个完整的、可运行的示例

下面是一个完整的 C 程序,它创建一个新线程,主线程和新线程分别打印自己的信息,并且主线程等待新线程结束后才退出。

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h> // 用于 sleep 函数
// 线程的入口函数
void* thread_function(void* arg) {
    int thread_num = *((int*)arg); // 将 void* 参数转换回 int*
    printf("Hello from thread number %d! (PID: %d, TID: %lu)\n", thread_num, getpid(), pthread_self());
    // 模拟一些工作
    sleep(2);
    printf("Thread number %d is finished.\n", thread_num);
    free(arg); // 释放为参数分配的内存
    return NULL; // 线程函数必须返回一个 void*
}
int main() {
    pthread_t my_thread;
    int thread_arg = 42; // 要传递给线程的参数
    printf("Main thread (PID: %d, TID: %lu) is creating a new thread...\n", getpid(), pthread_self());
    // 创建线程
    // 将 thread_arg 的地址作为参数传递
    if (pthread_create(&my_thread, NULL, thread_function, &thread_arg) != 0) {
        perror("Error creating thread");
        exit(EXIT_FAILURE);
    }
    printf("Main thread has created the new thread. The new thread's ID is %lu.\n", my_thread);
    // 等待新线程执行完毕
    // 如果没有这一行,主线程可能会在新线程开始前就退出了
    pthread_join(my_thread, NULL);
    printf("Main thread is finished.\n");
    return 0;
}

如何编译和运行?

因为使用了 Pthreads 库,你需要使用 -lpthread 选项来链接它。

# 编译
gcc my_program.c -o my_program -lpthread
# 运行
./my_program

预期输出:

Main thread (PID: 12345, TID: 140123456789120) is creating a new thread...
Main thread has created the new thread. The new thread's ID is 140123455774848.
Hello from thread number 42! (PID: 12345, TID: 140123455774848)
Thread number 42 is finished.
Main thread is finished.

(注意:PID 和 TID 的值每次运行都会不同)


重要注意事项

  1. 链接 Pthreads 库: 在 Linux 或 macOS 上编译时,必须加上 -lpthread,在 Windows 上,如果你使用 MinGW 或 Cygwin,也需要这样做,Visual Studio 用户通常需要在项目设置中链接 pthreadVC2.lib 或类似的库。
  2. 线程同步: 当多个线程访问共享资源(如全局变量、静态变量)时,如果不加以控制,会导致竞态条件,你需要使用互斥锁 (pthread_mutex_t)、条件变量 (pthread_cond_t) 等同步机制来确保数据的一致性。
  3. 内存管理: 通过 arg 传递的指针指向的内存必须在被访问的线程函数中保持有效,通常的做法是在主线程中 malloc 内存,在线程函数中 free 它,如果传递的是栈上变量的地址,那是非常危险的,因为主线程可能会继续执行并覆盖掉栈上的数据。
  4. 线程终止:
    • 线程函数执行到 return 语句会正常退出。
    • 调用 pthread_exit() 函数可以主动退出线程。
    • 一个线程可以被另一个线程通过 pthread_cancel() 取消(不推荐,因为可能导致资源未释放)。
  5. 资源回收: 必须确保在线程结束后回收其资源,最常用的方法是使用 pthread_join(),它会让主线程阻塞,直到目标线程结束,如果你不关心线程何时结束,也可以调用 pthread_detach() 将线程设置为分离状态,这样线程结束后会自动回收资源,无需 join
-- 展开阅读全文 --
头像
织梦dedecms xml地图插件怎么用?
« 上一篇 今天
dedecms多说评论插件如何安装使用?
下一篇 » 今天

相关文章

取消
微信二维码
支付宝二维码

目录[+]