linux C/C++多线程
一、什么是线程?
线程:是操作系统能够进行运算调度的最小单位。被包含在进程当中,是进程运行的最小单位。
主线程:当一个程序启动时,操作系统为其创建一个进程,同时创建的该进程的第一个线程,为该进程的主线程,此后创建的线程为该线程的子线程。每个进程,至少都有一个主线程(因为他要运行啊,哈哈,一个都没有可还行!!!)。
二、线程的生命周期
线程的生命周期分为创建、处理、与结束三个阶段,每个阶段都有与其相对应的处理动作。
线程处于的时期 | 常见的操作 |
---|---|
创建 | 向线程传递参数 |
运行 | 处理线程的业务逻辑 |
结束 | 传出线程结束状态信息或其他指定操作 |
三、线程操作常用的函数
1.创建线程
函数原型 | int pthread_create(pthread_t* restrict tidp, const pthread_attr_t* attr, void*(*start_rtn)(void * ), void * arg); |
---|---|
头文件 | <pthread.h> |
参数 | 说明 |
tidp | 指向保存新创建的线程id的内存单元 |
attr | 定制不同的线程属性 |
start_rtn | 新创建的线程开始运行的函数地址 |
arg | 向新创建线程传入的参数(参数大于一个时,以结构体形式传入) |
返回值 | 成功返回0,失败返回错误码 |
2.线程ID比较
函数原型 | int pthread_equal(pthread_t tid1, pthread_t itd2) |
---|---|
头文件 | <pthread.h> |
参数 | 说明 |
tid1 | 第一个线程id |
tid2 | 第二个线程id |
返回值 | 若相等,则返回非0值;否则,返回0 |
3.获取自身线程id
函数原型 | pthread_t pthread_self(void) |
---|---|
头文件 | <pthread.h> |
返回值 | 调用线程的线程id |
4.线程终止
函数原型 | void pthread_exit(void *rval_ptr) |
---|---|
头文件 | <pthread.h> |
参数 | 说明 |
rval_ptr | 无类型指针,与传给启动历程的单个参数类似。线程正常结束,则包含返回码,线程被取消,指向的内存单元置为PTHREAD_CANCELED |
5.将线程置于分离状态,等待线程的终止
函数原型 | int pthread_join(pthread_t thread, void** rval_ptr) |
---|---|
头文件 | <pthread.h> |
参数 | 说明 |
thread | 操作的线程id |
rval_ptr | 线程返回值,可置为NULL |
返回值 | 若线程已处于分离状态,返回EINVAL |
6.取消同一进程中的其他线程(并不等待线程终止)
函数原型 | int pthread_cancel(pthread_t tid); |
---|---|
头文件 | <pthread.h> |
参数 | 说明 |
tid | 要取消的线程id |
返回值 | 成功返回0,否则返回错误编号 |
四、线程相关注意事项
- 线程创建时并不能保证哪个线程(新创建的线程或调用线程)会先运行;
- 新创建的线程可访问进程的地址空间并继承调用线程的浮点环境和信号屏蔽字,但该线程的挂起信号集会被清除;
- pthread函数调用失败时通常会返回错误码(将错误范围限制在引起出错的函数),并提供errno的副本(与errno的现有函数兼容)。
- 线程的终止方式
位置 | 方式 |
---|---|
外部 | 统一进程中的任意线程调用了exit、_Exit或_exit,造成整个进程的终止 |
外部 | 被同一进程中的其他线程取消 |
内部 | 简单的从启动例程中返回,返回值为线程推出码 |
内部 | 线程调用pthread_exit自行终止 |
五、线程相关的部分细节
- 在使用g++编译多线程的代码时,需要手动加上 -lphread 参数。因为pthread库不是linux系统默认的库(libpthread.a)。
六、一个小例子
假设有个养猪的农民,每养成一头猪就会被屠夫杀掉,那么我们有两个线程,一个养猪的农民,一个杀猪的屠夫(由于例子中并无读写抢占的场景,故本着简单的原则,未设读写锁)。
简单粗暴的代码如下:
#include <iostream>
#include <pthread.h>
#include <vector>
#include <string.h>
typedef struct Pig
{
long long m_id;//猪的id
double m_weight;//猪的体重
char m_nick[200];//猪的外号
Pig()
{
memset(m_nick, 0x00, sizeof(m_nick));
}
Pig(long long id, double weight, const char* nick)
{
m_id = id;
m_weight = weight;
memset(m_nick, 0x00, sizeof(m_nick));
strncpy(m_nick, nick, strlen(nick));
}
struct Pig& operator=(const struct Pig& a_pig)
{
m_id = a_pig.m_id;
m_weight = a_pig.m_weight;
strcpy(m_nick, a_pig.m_nick);
return *this;
}
}pig;
std::vector<pig> pigs;//记录猪猪的信息
std::vector<pthread_t> threads;//记录线程id
void* slaugter(void* arg)
{
while (true)
{
if(!pigs.empty())//有猪猪便杀掉
{
pig l_pig = *pigs.begin();
pigs.erase(pigs.begin());
std::cout << " The " << l_pig.m_id << " pig " << l_pig.m_nick << " has been slaugtered "\
<< " to get " << l_pig.m_weight << " pounds meat " << std::endl;
}
}
}
void* farmer(void* arg)
{
std::vector<pthread_t>* ths = (std::vector<pthread_t>*)(arg);
while(true)
{
std::cout << " Input the pig's data: " << std::endl;
pig l_pig;
std::cin>> l_pig.m_id >> l_pig.m_weight >> l_pig.m_nick;
pigs.push_back(l_pig);
if(0 == l_pig.m_id)//猪猪编号为0,表示老农夫撂挑子不干了。
{
for(auto &th : *ths)
{
pthread_cancel(th);//终止所有非主线程
}
exit(0);//退出进程
}
}
}
int main()
{
pthread_t th;
pigs.clear();
threads.clear();
int err = pthread_create(&th, NULL, slaugter, NULL);//创建杀猪线程
if(err)
{
std::cout << " Create slaugter thread failed. " << std::endl;
return -1;
}
threads.push_back(th);
//将线程信息(线程id)传入养猪线程
err = pthread_create(&th, NULL, farmer, (void*)(&threads));
if(err)
{
std::cout << " Create farmer thread failed. " << std::endl;
return -1;
}
while(true);//防止子线程未启动,主线程提前退出。
//替换为下方注释代码,效果相同。
/*
for(auto &th : threads)
{
pthread_join(th, NULL);//调用线程将一直阻塞,等待线程返回。
}
*/
return 0;
}
创建linux_thread.cpp,写入上述代码,执行:
g++ linux_thread.cpp -lpthread -o linux_thread.out
./linux_thread.out
运行结果如下:
[pig@localhost code]$ g++ linux_thread.cpp -lpthread -o linux_thread.out
[pig@localhost code]$ ./linux_thread.out
Input the pig's data:
123 60.34 wooden_pig
Input the pig's data:
The 123 pig wooden_pig has been slaugtered to get 60.34 pounds meat
483 90.21 little_pig_pig
Input the pig's data: The
483 pig little_pig_pig has been slaugtered to get 90.21 pounds meat
0 0 0
The 0 pig 0 has been slaugtered to get 0 pounds meat
[pig@localhost code]$
由于线程调度问题,输出有些声东击西了,额,手动[笑cry]
有关程序中用到的运算符重载:C++运算符重载
此部分尚未完善,持续更新。文中有错误的地方,望广大有识之士予以指正,共同进步,谢谢^ _ ^…