pthread_create(3) Linux 手册页
一、 pthread_create(3) Linux 手册页:
man7.org > Linux > man-pages
Linux/UNIX 系统编程培训
pthread_create(3) — Linux 手册页
名称 (NAME) | 库 (LIBRARY) | 概要 (SYNOPSIS) | 描述 (DESCRIPTION) | 返回值 (RETURN VALUE) | 错误 (ERRORS) | 属性 (ATTRIBUTES) | 标准 (STANDARDS) | 历史 (HISTORY) | 注解 (NOTES) | 缺陷 (BUGS) | 示例 (EXAMPLES) | 参见 (SEE ALSO) | 跋 (COLOPHON)
名称 (NAME)
pthread_create - 创建一个新线程
库 (LIBRARY)
POSIX 线程库 (libpthread, -lpthread)
概要 (SYNOPSIS)
#include <pthread.h>
int pthread_create(pthread_t *restrict thread,
const pthread_attr_t *_Nullable restrict attr,
typeof(void *(void *_Nullable)) *start_routine,
void *_Nullable restrict arg);
描述 (DESCRIPTION)
pthread_create() 函数在调用它的进程中启动一个新线程。新线程通过调用 start_routine() 函数开始执行;arg 作为唯一参数传递给 start_routine()。
新线程将以下列方式之一终止:
- 它调用
pthread_exit(3),并指定一个退出状态值。同一进程中调用pthread_join(3)的另一个线程可以获取此值。 - 它从
start_routine()中返回。这等同于使用return语句中提供的值来调用pthread_exit(3)。 - 它被取消(参见
pthread_cancel(3))。 - 进程中的任何线程调用了
exit(3),或者主线程从main()中返回。这会导致进程中的所有线程终止。
attr 参数指向一个 pthread_attr_t 结构体,该结构体的内容在创建线程时用于决定新线程的属性;此结构体使用 pthread_attr_init(3) 及相关函数进行初始化。如果 attr 为 NULL,则使用默认属性创建线程。
在成功返回之前,pthread_create() 会将新线程的 ID 存储在由 thread 指向的缓冲区中;此标识符在后续调用其他 pthreads 函数时用于指代该线程。
新线程会继承创建它的线程的信号掩码副本 (pthread_sigmask(3))。新线程的挂起信号集为空 (sigpending(2))。新线程不会继承创建者的备用信号栈 (sigaltstack(2))。
新线程会继承调用线程的浮点环境 (fenv(3))。
新线程的 CPU 时间时钟初始值为 0(参见 pthread_getcpuclockid(3))。
Linux 专有细节
新线程继承调用线程的权能集合(capability sets,参见 capabilities(7))和 CPU 亲和力掩码(CPU affinity mask,参见 sched_setaffinity(2))的副本。
返回值 (RETURN VALUE)
成功时,pthread_create() 返回 0;出错时,它返回一个错误编号(error number),并且 *thread 的内容处于未定义状态。
错误 (ERRORS)
- EAGAIN:系统资源不足,无法创建另一个线程。
- EAGAIN:遇到了系统对线程数量施加的限制。可能触发此错误的限制包括:达到了由
setrlimit(2)设置的实际用户 ID 所能拥有的进程和线程数的软限制 (RLIMIT_NPROC);达到了内核系统级最大进程和线程数限制/proc/sys/kernel/threads-max(参见proc(5));或达到了 PID 的最大数量限制/proc/sys/kernel/pid_max(参见proc(5))。 - EINVAL:
attr中的设置无效。 - EPERM:没有权限设置
attr中指定的调度策略和参数。
属性 (ATTRIBUTES)
有关本节中使用的术语解释,请参见 attributes(7)。
| 接口 | 属性 | 值 |
|---|---|---|
pthread_create() |
线程安全性 (Thread safety) | MT-Safe (多线程安全) |
标准 (STANDARDS)
POSIX.1-2008.
历史 (HISTORY)
POSIX.1-2001.
注解 (NOTES)
有关 pthread_create() 返回在 *thread 中的线程 ID 的进一步信息,请参见 pthread_self(3)。除非使用了实时调度策略,否则在调用 pthread_create() 之后,接下来哪个线程(是调用者还是新线程)将被执行是不确定的。
线程可以是可连接的 (joinable) 或 分离的 (detached)。如果一个线程是可连接的,那么另一个线程可以调用 pthread_join(3) 来等待该线程终止并获取其退出状态。只有当已终止的可连接线程被连接 (joined) 后,其最后剩下的系统资源才会被释放回系统。当一个分离的线程终止时,其资源会自动释放回系统:无法连接分离的线程来获取其退出状态。将线程设为分离状态对于某些后台守护 (daemon) 线程非常有用,因为应用程序不需要关心它们的退出状态。默认情况下,新线程是在可连接状态下创建的,除非 attr 被设置为在分离状态下创建线程(使用 pthread_attr_setdetachstate(3))。
在 NPTL 线程实现下,如果在程序启动时 RLIMIT_STACK 软资源限制的任何值非 “unlimited”(无限制),那么它将决定新线程的默认栈大小。通过使用 pthread_attr_setstacksize(3),可以在用于创建线程的 attr 参数中显式设置栈大小属性,以获得不同于默认值的栈大小。如果 RLIMIT_STACK 资源限制被设置为 “unlimited”,则会使用特定于架构的值作为栈大小:大多数架构为 2 MB;POWER 和 Sparc-64 架构为 4 MB。
缺陷 (BUGS)
在已废弃的 LinuxThreads 实现中,同一个进程中的每个线程都有不同的进程 ID。这违反了 POSIX 线程规范,也是该实现中许多其他不符合标准问题的根源;参见 pthreads(7)。
示例 (EXAMPLES)
下面的程序演示了 pthread_create() 以及 pthreads API 中一些其他函数的使用方法。
在以下运行示例中(在提供 NPTL 线程实现的系统上),栈大小默认为 “stack size” (栈大小) 资源限制所给定的值:
$ ulimit -s
8192 # 栈大小限制为 8 MB (0x800000 字节)
$ ./a.out hola salut servus
Thread 1: top of stack near 0xb7dd03b8; argv_string=hola
Thread 2: top of stack near 0xb75cf3b8; argv_string=salut
Thread 3: top of stack near 0xb6dce3b8; argv_string=servus
Joined with thread 1; returned value was HOLA
Joined with thread 2; returned value was SALUT
Joined with thread 3; returned value was SERVUS
在下一次运行中,程序显式地为创建的线程设置了 1 MB 的栈大小(使用 pthread_attr_setstacksize(3)):
$ ./a.out -s 0x100000 hola salut servus
Thread 1: top of stack near 0xb7d723b8; argv_string=hola
Thread 2: top of stack near 0xb7c713b8; argv_string=salut
Thread 3: top of stack near 0xb7b703b8; argv_string=servus
Joined with thread 1; returned value was HOLA
Joined with thread 2; returned value was SALUT
Joined with thread 3; returned value was SERVUS
程序源码
#include <ctype.h>
#include <err.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
struct thread_info { /* 用于作为 thread_start() 的参数 */
pthread_t thread_id; /* 由 pthread_create() 返回的 ID */
int thread_num; /* 应用程序定义的线程编号 */
char *argv_string; /* 来源于命令行参数 */
};
/* 线程启动函数:显示栈顶附近的地址,
并返回 argv_string 的大写副本。 */
static void *
thread_start(void *arg)
{
struct thread_info *tinfo = arg;
char *uargv;
printf("Thread %d: top of stack near %p; argv_string=%s\n",
tinfo->thread_num, (void *) &tinfo, tinfo->argv_string);
uargv = strdup(tinfo->argv_string);
if (uargv == NULL)
err(EXIT_FAILURE, "strdup");
for (char *p = uargv; *p != '\0'; p++)
*p = toupper(*p);
return uargv;
}
int
main(int argc, char *argv[])
{
int s, opt;
void *res;
size_t num_threads;
ssize_t stack_size;
pthread_attr_t attr;
struct thread_info *tinfo;
/* "-s" 选项为我们的线程指定栈大小。 */
stack_size = -1;
while ((opt = getopt(argc, argv, "s:")) != -1) {
switch (opt) {
case 's':
stack_size = strtoul(optarg, NULL, 0);
break;
default:
fprintf(stderr, "Usage: %s [-s stack-size] arg...\n",
argv[0]);
exit(EXIT_FAILURE);
}
}
num_threads = argc - optind;
/* 初始化线程创建属性。 */
s = pthread_attr_init(&attr);
if (s != 0)
errc(EXIT_FAILURE, s, "pthread_attr_init");
if (stack_size > 0) {
s = pthread_attr_setstacksize(&attr, stack_size);
if (s != 0)
errc(EXIT_FAILURE, s, "pthread_attr_setstacksize");
}
/* 为 pthread_create() 的参数分配内存。 */
tinfo = calloc(num_threads, sizeof(*tinfo));
if (tinfo == NULL)
err(EXIT_FAILURE, "calloc");
/* 为每个命令行参数创建一个线程。 */
for (size_t tnum = 0; tnum < num_threads; tnum++) {
tinfo[tnum].thread_num = tnum + 1;
tinfo[tnum].argv_string = argv[optind + tnum];
/* pthread_create() 调用将线程 ID 存储到对应的 tinfo[] 元素中。 */
s = pthread_create(&tinfo[tnum].thread_id, &attr,
&thread_start, &tinfo[tnum]);
if (s != 0)
errc(EXIT_FAILURE, s, "pthread_create");
}
/* 销毁线程属性对象,因为它不再需要了。 */
s = pthread_attr_destroy(&attr);
if (s != 0)
errc(EXIT_FAILURE, s, "pthread_attr_destroy");
/* 现在连接 (join) 每个线程,并显示其返回值。 */
for (size_t tnum = 0; tnum < num_threads; tnum++) {
s = pthread_join(tinfo[tnum].thread_id, &res);
if (s != 0)
errc(EXIT_FAILURE, s, "pthread_join");
printf("Joined with thread %d; returned value was %s\n",
tinfo[tnum].thread_num, (char *) res);
free(res); /* 释放由线程分配的内存 */
}
free(tinfo);
exit(EXIT_SUCCESS);
}
参见 (SEE ALSO)
getrlimit(2), pthread_attr_init(3), pthread_cancel(3), pthread_detach(3), pthread_equal(3), pthread_exit(3), pthread_getattr_np(3), pthread_join(3), pthread_self(3), pthread_setattr_default_np(3), pthreads(7)
跋 (COLOPHON)
本页面是 man-pages(Linux 内核与 C 库用户空间接口文档)项目的一部分。关于该项目的信息可以前往 ⟨https://www.kernel.org/doc/man-pages/⟩ 了解。如果您发现本手册页有漏洞或错误,请参阅 ⟨https://git.kernel.org/pub/scm/docs/man-pages/man-pages.git/tree/CONTRIBUTING⟩。
此页面是从 man-pages-6.18.tar.gz 压缩包中获取的,该包于 2026-05-24 获取自 ⟨https://mirrors.edge.kernel.org/pub/linux/docs/man-pages/⟩。如果您在此 HTML 版本的页面中发现任何渲染问题,或者您认为有更好、更新的页面源码,又或者您想要对本跋(此部分不属于原始手册页)中的信息提供更正或改进,请发送邮件至 man-pages@man7.org。
Linux man-pages 6.18 | 2026-02-10 | pthread_create(3)
引用了此页面的其他页面:mmap(2), spu_run(2), wait(2), pthread_attr_init(3), pthread_attr_setdetachstate(3), pthread_attr_setguardsize(3), pthread_attr_setinheritsched(3), pthread_attr_setschedparam(3), pthread_attr_setschedpolicy(3), pthread_attr_setscope(3), pthread_attr_setstack(3), pthread_attr_setstackaddr(3), pthread_attr_setstacksize(3), pthread_cancel(3), pthread_detach(3), pthread_equal(3), pthread_exit(3), pthread_getattr_default_np(3), pthread_getattr_np(3), pthread_join(3), pthread_key_create(3), pthread_self(3), pthread_setaffinity_np(3), pthread_setname_np(3), pthread_setschedparam(3), pthread_setschedprio(3), pthread_sigmask(3), pthreads(7)
HTML 渲染由 Michael Kerrisk 于 2026-05-30 创建,《The Linux Programming Interface》的作者。
二、上面示例程序的介绍
解释这段 C 多线程程序。这段程序是一个 POSIX 多线程(pthread)示例,展示了如何创建多个线程、传递参数并收集返回值。我先给你一张整体流程图,再逐部分解释。> 点击图中任意节点可深入追问某一步。
整体目标
这段程序的功能很简单:为每个命令行参数启动一个线程,每个线程把对应参数转成大写并返回,主线程最后打印所有结果。
各部分详解
1. 数据结构 thread_info
struct thread_info {
pthread_t thread_id; // pthread_create() 返回的线程句柄
int thread_num; // 可读的线程编号(1, 2, 3...)
char *argv_string; // 指向命令行参数的指针
};
这是「线程参数包」,用于向 pthread_create() 传递多个数据。pthread_create 只接受一个 void * 参数,所以把所有信息打包成结构体是标准做法。
2. 线程函数 thread_start
static void * thread_start(void *arg)
每个线程运行的函数,做三件事:
- 打印栈地址:
&tinfo是局部变量的地址,近似反映这个线程的栈顶位置,可用来观察不同线程栈分布在内存哪里。 - 复制字符串:用
strdup()分配新内存(堆上),避免修改原始参数。 - 转大写:遍历字符并调用
toupper(),最后return uargv——这个指针就是线程的"返回值",由pthread_join取回。
3. main 中的参数解析
while ((opt = getopt(argc, argv, "s:")) != -1) { ... }
getopt 处理 -s <数字> 选项,允许用户自定义线程栈大小。optind 之后的参数都是要处理的字符串。
4. 线程属性设置
pthread_attr_init(&attr);
pthread_attr_setstacksize(&attr, stack_size); // 可选
pthread_attr_t 是一个配置对象,可设置栈大小、调度策略等。创建完所有线程后就 pthread_attr_destroy 了——因为属性只在 创建时 用到,之后不再需要。
5. 创建线程
pthread_create(&tinfo[tnum].thread_id, &attr, &thread_start, &tinfo[tnum]);
四个参数依次是:
| 参数 | 含义 |
|---|---|
&tinfo[tnum].thread_id |
输出:存储新线程的 ID |
&attr |
线程属性(栈大小等) |
&thread_start |
线程入口函数 |
&tinfo[tnum] |
传给入口函数的参数 |
6. 等待线程并收集结果
pthread_join(tinfo[tnum].thread_id, &res);
printf("... %s\n", (char *) res);
free(res);
pthread_join 有两个作用:
- 阻塞等待:主线程暂停,直到指定线程结束。
- 取回返回值:
res接收线程return的指针——即线程里strdup分配的大写字符串。
注意,这块内存是线程分配的(在堆上),主线程取回后必须 free,否则内存泄漏。
核心要点总结
| 概念 | 说明 |
|---|---|
pthread_create |
创建线程,传参通过 void * |
thread_start 返回 void * |
线程可以"返回值",本质是个指针 |
pthread_join |
同步线程 + 获取返回值 |
strdup / free |
线程在堆上分配内存,主线程负责释放 |
pthread_attr_t |
可选配置(如自定义栈大小) |
三、getopt函数
先看代码本身,再配两张图帮你理解。
代码回顾
stack_size = -1;
while ((opt = getopt(argc, argv, "s:")) != -1) {
switch (opt) {
case 's':
stack_size = strtoul(optarg, NULL, 0);
break;
default:
fprintf(stderr, "Usage: %s [-s stack-size] arg...\n", argv[0]);
exit(EXIT_FAILURE);
}
}
num_threads = argc - optind;
getopt 是什么?
getopt 是 POSIX 标准库函数,专门用来解析 Unix 风格的命令行选项(-x、-s 4096 这类)。它每次调用只处理一个选项,所以要放在 while 循环里反复调用直到返回 -1(表示没有更多选项了)。
函数签名:
int getopt(int argc, char *const argv[], const char *optstring);
第三个参数 "s:" 是选项字符串,含义是:
s— 程序接受-s这个选项:— 紧跟在s后面的冒号表示-s需要一个参数(如果不带冒号则不需要参数)
每次 getopt 返回时:
- 返回识别到的选项字母(如
's') - 把该选项的参数存入全局变量
optarg - 遇到未知选项时返回
'?' - 所有选项处理完毕时返回
-1
下图展示 getopt 每轮循环在 argv 数组里的扫描过程:接下来看 while 循环每一轮的决策逻辑:—
各行代码的具体含义
stack_size = -1:初始化为 -1 作为"未设置"的哨兵值,后面用 if (stack_size > 0) 来判断用户是否提供了 -s。
while ((opt = getopt(argc, argv, "s:")) != -1):这是一个赋值 + 比较合并写法。getopt 的返回值先赋给 opt,同一表达式里再与 -1 比较。等价于:
opt = getopt(argc, argv, "s:");
while (opt != -1) { ... opt = getopt(argc, argv, "s:"); }
case 's': stack_size = strtoul(optarg, NULL, 0):optarg 是 getopt.h 提供的全局指针,自动指向当前选项的参数字符串(这里是 "4096" 这样的字符串)。strtoul 把它转换成无符号长整型,第三个参数 0 表示自动推断进制(0x 开头=十六进制,0 开头=八进制,其他=十进制)。
default: fprintf(stderr, ...); exit(EXIT_FAILURE):getopt 遇到选项字符串里没有的选项字母时返回 '?',由 default 捕获,向标准错误输出使用说明然后退出。
num_threads = argc - optind:optind 是另一个全局变量,getopt 处理完所有选项后,它指向 argv 中第一个非选项参数的下标。argc - optind 就是剩余参数的个数,也就是要创建的线程数。
一句话总结
getopt 这段代码做的事就是:把 -s 4096 hello world 这样的命令行拆成两部分——配置项(-s 4096)和数据(hello world),配置项交给 stack_size 保存,数据个数交给 num_threads 保存,之后用 argv[optind + tnum] 逐个访问每个数据参数。
更多推荐

所有评论(0)