线程概述
栗子:
有一家大工厂,有很多个厂房,每一个厂房都可以独立工作,多个厂房则效率更高。厂商根据市场需求调整要开几间厂房进行生产,如果需求不高,就保存一间厂房生产即可,需求高就多开几间。
这里:
一个厂房——一个进程
一个厂房里面的一个工序——线程
进程是操作系统中的一个基本单元,我们整个系统是由无数个进程组成
线程是操作系统运行的最小单元,每一个进程如果不做处理,默认就有一个线程,叫主线程
一个进程如果有多个线程,则除了主线程之外其他线程叫副线程
进程是资源分配的基本单位,线程是调度的基本单位
在linux中,内核把线程和进程一样来管理,线程是轻量级进程,内核只认识pcb
下面介绍一些linux下线程的特性,并与进程区别
- linux支持多线程,一个进程可以拥有多个线程,多线程是除了多进程外另一种并发方式
- 各个进程之间有父子或兄弟关系,各个副线程之间没有父子关系,线程只有主从关系
- 主线程和副线程共享:.text段,.bss段 .data段, 堆,动态库加载区。不共享:栈不共享(如果创建5个线程的话,这5个线程平均分配这个栈)
- 进程间通讯必须经过操作系统,而线程间通讯不必经过操作系统,线程间通信是通过:堆和全局变量。
- 线程的优点:创建代价小,线程见切换比进程间切换方便,线程占用资源比进程少
- 线程的缺点:共享变量操作过程中造成的不确定性,要考虑互斥量文件锁等操作,多线程难以调试
- 多线程环境中,主线程终止,全部子线程被迫终止(没有了资源),这一点不像僵尸进程或者孤儿进程
更多线程理论概念可参考
Linux-线程学习(上)
线程标识符pthread_t:
- 像每个进程有一个进程ID一样,每个线程也有一个线程ID
- 进程ID在整个系统中是唯一的,但线程不同,线程ID只在它所属的进程环境中有效
- 线程ID用pthread_t数据类型来表示,实现的时候可以用一个结构来代表pthread_t数据类型,所以可以移植的操作系统不能把它作为整数处理
实战注意事项
- 与线程有关的函数构成了一个完整的系列,绝大多数函数的名字都是以“pthread_”打头的
- 要使用这些函数库,要通过引入头文件<pthread.h>
- 链接这些线程函数库时要使用编译器命令的“-lpthread”选项
在vs+visualGDB下增加pthread编译方法:
注意:每次新建工程都要记得配置,必须选定一个工程再点GDB选项才有反应不然选项弹窗不会弹出!
在ubuntu+codeblock下增加pthread编译方法:
线程系统函数
pthread_create
作用:创建一个新的线程
int pthread_create(pthread_t *thread,
pthread_attr_t *attr,
void*(*start_routine)(void*),
void *arg);
• thread:新线程创建成功后,保存新线程的标识符
• attr:设置线程的属性,一般不需要什么特殊的属性,直接传 NULL即可
Posix线程中的线程属性pthread_attr_t主要包括detach属性、policy属性、优先级、继承属性、堆栈地址、scope属性、堆栈大小。在pthread_create中,把第二个参数设置为NULL的话,将采用默认的属性配置。
typedef unsigned long int pthread_t;
在/usr/include/i386-linux-gnu/bits/pthreadtypes.h定义的
joinable状态和unjoinable状态,如果线程是joinable状态,当线程函数自己返回退出时或pthread_exit时都不会释放线程所占用堆栈和线程描述符(总计8K多)。只有当你调用了pthread_join之后这些资源才会被释放。若是unjoinable状态的线程,这些资源在线程函数退出时或pthread_exit时自动会被释放。
• start_routine: 是个函数地址,线程启动后要执行的函数
• arg:传给线程启动函数的参数
返回值:成功返回0,失败返回 错误码
pthread_exit
作用:结束调用了这个函数的线程
void pthread_exit (void *retbal);
返回值:返回一个指向某个对象的指针。
- 绝不要用它返回一个指向一个局部变量的指针
- 线程在结束时必须调用pthread_exit函数,这与一个进程在结束时要调用exit函数是同样的道理
pthread_join
作用:在线程结束后把它们归并到一起,pthread_join相当于进程用来等待子进程的wait函数
int pthread_join(pthread_t th, void **thread_return);
- th: 指定了将要等待的线程标识符
- thread_return: 它指向另外一个指针,而后者指向线程的返回值
返回值:
成功时返回“0”,失败时返回一个错误代码
更多函数:可见
linux线程的创建与使用
代码实例
代码功能:
1、创建线程,传输参数stu_info,并在副线程打印出来
2、采用全局变量global_number在主线程和副线程都打印,验证共享数据
3、注释和不注释pthread_join,验证同时运行和先后运行现象
代码如下:
#include <iostream>
#include <pthread.h>//pthread_create
#include <stdio.h>//perror
#include <unistd.h>//sleep
using namespace std;
int global_number = 10;
typedef struct
{
int id;
}STU;
void *thread_fun(void *data)//data获得pthread_create的第四个参数传的数据
{
STU *stu_info = (STU*)data;
cout << "stu_info" << stu_info->id << endl;
for(int i=0;i<5;i++)
{
global_number++;
cout << "thread runnning..." << global_number<<endl;
sleep(1);
}
}
int main(int argc, char *argv[])
{
pthread_t pthread_id;
STU stu_info;
stu_info.id = 1001;
if (pthread_create(&pthread_id, NULL, thread_fun, &stu_info) < 0)//2指定线程属性3函数指针4给线程传递的参数
{
perror("pthread create err:");
}
//线程合并,类似于wait
//pthread_join(pthread_id,NULL);
for (int i = 0; i < 5; i++)
{
global_number++;
cout << "main runnning..." << global_number<< endl;
sleep(1);
}
return 0;
}
首先我们不加pthread_join,让主线程和副线程同时对全局number++,观察输出
可以看到:
1、确实可以通过pthread_create第四个参数传参,打印出了1001
2、主线程和副线程交替进行,共同对number++
现在我们加上pthread_join,让主线程等待副线程运行结束再往下运行,观察输出
可见join可控制指定线程先走,进而达到线程顺序选择的目的
end