创建(删除)一个新线程的代价要比创建一个新进程小得多。
切换:与进程之间的切换(丢弃cache,重新进行命中缓存 )相比,线程之间的切换(不需要对cache进行切换)需要操作系统做的工作要少很多。
一、 创建线程
pthread_create 创建一个POSIX 线程(Linux 下常用轻量级线程),操作系统调度新线程执行指定入口函数,主线程与新线程并发运行。

参数说明
pthread_t *thread指针,函数成功后会把新线程的唯一 ID 写入该变量,后续pthread_join/pthread_detach靠此 ID 操作线程。pthread_attr_t *attr线程属性结构体(栈大小、分离状态、调度优先级等),传NULL采用系统默认配置。void *(*start_routine)(void *)线程执行函数,格式固定 :接收一个 void参数,返回 void。void *arg传递给线程函数的唯一参数,多参数可封装到结构体再传结构体指针。
cpp
#include <iostream>
#include <cstdio>
#include <string>
#include <pthread.h>
#include <unistd.h>
void *Routine(void *args)
{
std::string name = static_cast<const char *>(args);
printf("--------------------------------------------------\n");
while (true)
{
// std::cout<<"我是新线程:"<<name<<std::endl;
printf("我是新线程:%s,tid:0x%lx,pid:%d\n", name.c_str(), pthread_self(), getpid());
sleep(1);
}
}
int main()
{
int n;
const int num = 10;
for (int i = 0; i < num; i++)
{
pthread_t tid;
char threadname[64];
snprintf(threadname, sizeof(threadname), "thread-%d", i + 1);
pthread_create(&tid, nullptr, Routine, threadname);
(void)n;
usleep(1);
}
// pthread_t tid;
// int n=pthread_create(&tid,nullptr,Routine,(void*)"thread-1");
// (void)n;
// printf("new thread id :0x%lx\n",tid);
// while(true)
// {
// std::cout<<"我是主线程...."<<std::endl;
// sleep(1);
//}
while (true)
{
// std::cout<<"我是主线程...."<<std::endl;
printf("我是主线程tid:0x%lx\n", pthread_self());
sleep(1);
}
return 0;
}
二、线程终止
如果需要只终止某个线程而不终止整个进程,可以有三种方法:
- 从线程函数return。这种方法对主线程不适⽤,从main函数return相当于调用exit。
- 线程可以调用pthread_ exit终止自己。
- ⼀个线程可以调用pthread_ cancel终止同⼀进程中的另一个线程。
三、线程等待
pthread_join

你的代码不加它会发生什么
你现在 main 创建线程后直接结束:
- main 函数执行完毕 → 进程直接退出
- OS 会强制杀死所有子线程
- 子线程循环打印大概率只会输出一两行甚至完全不输出
加上pthread_join(tid, nullptr);后: 主线程停在这里死等子线程,子线程循环跑,不会被提前杀掉
调用 pthread_join 的线程会阻塞挂起,直到参数中指定 ID 的目标线程运行终止。 目标线程的终止方式不同,pthread_join 通过第二个参数 value_ptr 传出的终止状态也不一样,分四种情况:
- 目标线程通过
return正常返回:value_ptr指向的内存中,保存线程函数return的返回值。 - 目标线程被其他线程调用
pthread_cancel取消终止:value_ptr指向的内存中,存入常量PTHREAD_CANCELED。 - 目标线程自身调用
pthread_exit主动退出:value_ptr指向的内存中,存放传入pthread_exit的参数。 - 不需要获取线程终止状态:直接给
value_ptr传入NULL即可。
cpp
#include <iostream>
#include <cstdio>
#include <string>
#include <pthread.h>
#include <unistd.h>
class Res
{
public:
int code;
std::string name;
std::string info;
};
void *Routine(void *args)
{
std::string name = static_cast<const char *>(args);
while (true)
{
std::cout << "new thread" << std::endl;
sleep(1);
break;
}
Res *res = new Res();
res->code = 10;
res->name = name;
res->info = "我这个线程以及完蛋了,你们继续努力";
return (void *)res;
}
int main()
{
pthread_t tid;
pthread_create(&tid, nullptr, Routine, (void *)"thread-1");
sleep(10);
// 1、一般而言必须等待新线程退出,如果不等待,就会形成类似僵尸问题
// 2、为了获取新线程执行的结果
void *retval = nullptr;
// 新线程出异常,进程就会全部退出,没机会join成功!不需要关心异常
int n = pthread_join(tid, (void **)&retval); // 只关心正常情况
if (n == 0)
{
Res *p=(Res*)retval;
std::cout << "join success" << p->code<< std::endl;
std::cout << "join success" << p->name << std::endl;
std::cout << "join success" << p->info << std::endl;
delete p;
}
return 0;
}
四、分离线程
默认 情况下,新创建的线程是joinable的。线程退出后,需要对其进行pthread_join操作,否则无法释放资源,从而造成系统泄露
如果不关心线程的返回值,join是一种负担,这个时候我们可以告诉系统,当线程退出时,自动释放线程资源。(线程被分离)
线程创建后默认是 joinable(可汇合) 状态: 代表这个线程退出后,内核会保留它的资源、退出码,必须通过 pthread_join() 回收,否则会变成僵尸线程。
等待线程,是阻塞等待的,想让线程全部自动结束掉,主进程不想等待,把目标被等待的线程设置为:分离状态。
一旦线程分离后,主线程就不管了。 分离是一种状态。
pthread_detach

返回值

cpp
#include <iostream>
#include <cstdio>
#include <string>
#include <pthread.h>
#include <unistd.h>
class Res
{
public:
int code;
std::string name;
std::string info;
};
void *Routine(void *args)
{
int cnt=5;
while(cnt)
{
std::cout<<"新线程在运行"<<cnt--<<std::endl;
sleep(1);
}
return nullptr;
}
int main()
{
pthread_t tid;
pthread_create(&tid, nullptr, Routine, (void *)"thread-1");
std::cout<<"main thread 分离线程"<<std::endl;
pthread_detach(tid);
sleep(10);
}
执行结果是:

线程ID的理解及进程地址空间布局



线程局部存储
cpp
#include <iostream>
#include <cstdio>
#include <string>
#include <pthread.h>
#include <unistd.h>
__thread int id=0;
void *routine(void *args)
{
std::string name = static_cast<const char *>(args);
while (true)
{
std::cout << "new thread id : " << id << std::endl;
id++;
sleep(1);
}
return nullptr;
}
int main()
{
pthread_t tid;
pthread_create(&tid, nullptr, routine, (void *)"thread-1");
while (true)
{
std::cout << "main thread id : " << id << std::endl;
sleep(1);
}
pthread_join(tid, nullptr);
return 0;
}
运行结果是:

从这里可以看到,在同一个全局变量,内核会给每一个创建出来的线程,各自分配一份独立的变量副本。子线程操作的是自己专属的id,主线程操作的是自己的专属id两份内存互相不占用、互相不修改。
要让线程局部存储:1、加__thread(两个_),只能修饰全局变量或者函数内static静态变量
2、变量每个线程各自一份副本,互相完全隔离
3、声明格式:__thread 数据类型 变量名=初始值;