目录
1、线程介绍
概念:线程是一个轻量级的进程,每一个线程都属于一个进程,线程是CPU任务调度的最小单元,线程为进程中的栈里面的0-8M的一小段内存空间依附于进程之上。
进程是操作系统分配资源的单位,线程是调度的基本单位,线程之间共享进程资源。
创建:线程空间位于进程空间内部,进程中的每个线程,栈区是独立的,共享进程中的数据区和文本区,堆区(待补充完善)。
调度:宏观并行、微观串行。
并发与并⾏
并⾏:指两个或多个事件在同⼀时刻发⽣(同时发⽣)。
并发:指两个或多个事件在同⼀个时间段内发⽣。
在操作系统中,安装了多个程序,并发指的是在⼀段时间内宏观上有多个程序同时运⾏,这在单 CPU 系统中,每⼀时刻只能有⼀道程序执⾏,即微观上这些程序是分时的交替运⾏,只不过是给⼈的感觉是同时运 ⾏,那是因为分时交替运⾏的时间是⾮常短的。
⽽在多个 CPU 系统中,则这些可以并发执⾏的程序便可以分配到多个处理器上( CPU ),实现多任务并⾏ 执⾏,即利⽤每个处理器来处理⼀个可以并发执⾏的程序,这样多个程序便可以同时执⾏。⽬前电脑市场 上说的多核 CPU ,便是多核处理器,核 越多,并⾏处理的程序越多,能⼤⼤的提⾼电脑运⾏的效率。
注意:单核处理器的计算机肯定是不能并⾏的处理多个任务的,只能是多个任务在单个 CPU 上并发运 ⾏。同理 , 线程也是⼀样的,从宏观⻆度上理解线程是并⾏运⾏的,但是从微观⻆度上分析却是串⾏ 运⾏的,即⼀个线程⼀个线程的去运⾏,当系统只有⼀个 CPU 时,线程会以某种顺序执⾏多个线程, 我们把这种情况称之为线程调度。
线程消亡:线程执行结束后,需要回收线程空间。
多进程和多线程对比:
多进程 | 多线程 | |
执行效率 | 多进程执行效率低 | 多线程执行效率高: 并发程度:创建速度快 任务切换:在一个进程空间内部完成多线程任务切换 |
通信角度 | 进程空间独立,不能直接通信(通信必须使用:管道、信号、消息队列、共享内存、信号灯等方式) | 线程可以共享空间,可以直接通信(多线程间可以使用全局变量通信,因为全局变量为共享空间) |
安全性 | 多进程安全:一个进程异常结束不影响其余进程执行 | 多线程不安全:一个线程异常结束会导致进程结束,其余线程均无法继续执行 |
2、接口介绍
2.1、线程创建:pthread_create
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,void *(*start_routine) (void *), void *arg);
头文件:pthread.h
功能:创建一个线程
参数:
thread:存放线程ID空间首地址(线程编号)
attr:线程的属性(默认NULL)
start_routine:线程入口函数地址(函数指针)
arg:给线程函数的参数(如果不传值进入,则为NULL)
返回值:
成功返回0
失败返回非0值
void *th_fun(void *arg)
{
while(1) {
printf("This is a thread\n");
sleep(1);
}
}
int main(void)
{
pthread_t tid;
pthread_create(&tid, NULL, th_fun, NULL);
return 0;
}
以上代码便实现了创建一个线程的工作,在线程中进行输出This is a thread的工作,由于线程占用系统的空间并不大,所以往往都是在子线程中完成各个小分支的项目需求,而主线程未接收控制子线程,这点和进程有所区别。
线程创建注意:
- 一次pthread_create执行只能创建一个线程。
- 每个进程至少有一个线程称为主线程。
- 主进程退出则所有创建的子线程都退出。
- 主线程必须有子线程同时运行才算多线程程序。
- 线程id是线程的唯一标识,是CPU维护的一组数字。
- pstree 查看系统中多线程的对应关系。(ps -eLf )
- 多个子线程可以执行同一回调函数。
2.2、获取线程ID:pthread_self
pthread_t pthread_self(void);
- 成功 返回当前线程的线程id
- 失败 -1
void *th_fun(void *arg)
{
while(1) {
printf("This thread's tid = %lu\n", pthread_self());
sleep(1);
}
}
int main(void)
{
pthread_t tid;
pthread_create(&tid, NULL, th_fun, NULL);
return 0;
}
2.3、线程退出:pthread_exit
void pthread_exit(void *retval);
功能:子线程自行退出
参数:retval--线程退出时候的返回状态,临死遗言。(不能是局部变量的地址),没有想传送的就默认为NULL
返回值:无
void *th_fun1(void *arg)
{
int cnt = 10;
while(1) {
if(cnt == 0) {
pthread_exit(NULL);
}
cnt--;
printf("in thread fun. tid1 = %lu\n", pthread_self());
sleep(1);
}
}
int main(void)
{
pthread_t tid;
pthread_create(&tid, NULL, th_fun, NULL);
return 0;
}
以上代码为子线程的一个函数,输出子线程号10次以后自动退出
2.4、请求取消线程:pthread_cancel
int pthread_cancel(pthread_t thread);
- 成功返回0
- 失败返回-1;
2.5、线程资源回收:pthread_join
int pthread_join(pthread_t thread, void **retval);
功能:用于等待其他线程结束,当调用pthread_join时,当前线程会处于阻塞状态,知道被调用的线程结束后,当前线程才会重新开始执行。
参数:
pthread_t thread: 被连接线程的线程号
void **retval : 指向一个指向被连接线程的返回码的指针的指针
返回值:
线程连接的状态,0是成功,非0是失败
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include <unistd.h>
#include <pthread.h>
#include <sys/types.h>
static int count = 0;
void* thread_run(void* parm)
{
for (int i=0;i<5;i++) {
count++;
printf("The thread_run method count is = %d\n",count);
sleep(1);
}
// return NULL;
}
int main(int argc, const char *argv[])
{
pthread_t tid;
pthread_create(&tid, NULL, thread_run,NULL);
// 加入pthread_join后,主线程"main"会一直等待直到tid这个线程执行完毕自己才结束
// 一般项目中需要子线程计算后的值就需要加join方法
// pthread_join(tid,NULL);
// 如果没有join方法可以看看打印的顺序
printf("The count is = %d\n",count);
getchar();
return 0;
}
3、线程的属性
3.1、属性介绍
- 可结合性:(默认)能被其他线程回收和杀死的线程具有可结合性,在没有被其他线程回收之前,其资源不释放,使用pthread_join()函数回收。(类似于僵尸进程)
- 分离属性:不能被其他线程回收和杀死的线程具有分离属性,其存储资源在终止时被系统自动释放。(类似于孤儿进程)
3.2、线程属性的设置
方法1:
- pthread_attr_init
int pthread_attr_init(pthread_attr_t *attr);
功能:初始化一个attr的变量
参数:attr,需要变量来接受初始值
返回值:
0 --->成功,
非0---> 错误;
- pthread_attr_setdetachstate
int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate);
功能:把一个线程设置成相应的属性
参数:attr,属性变量(init函数初始化创立的)
detachstate:有2个可选值,
PTHREAD_CREATE_JOINABLE:可结合性(可以用0来替代)
PTHREAD_CREATE_DETACHED:设置分离属性(可以用1来替代)
pthread_attr_destroy
- int pthread_attr_destroy(pthread_attr_t *attr);
功能:销毁线程属性
代码示例:
void *th_fun(void *arg)
{
while(1) {
printf("in thread fun.\n");
sleep(1);
}
}
int main(int argc, const char *argv[])
{
pthread_t tid;
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, 1);
pthread_create(&tid, &attr, th_fun, NULL);
pthread_attr_destroy(&attr);
while(1) {
printf("in thread.\n");
sleep(1);
}
return 0;
}
以上代码即可正常执行,无需关心次线程的回收以及调用,程序退出以后会被系统自动回收
方法2:
pthread_detach (在次线程中设置)
int pthread_detach(pthread_t thread);
功能:设置分离属性
参数:线程id号,填自己的id
通过以上代码我们可以实现线程创立以及属性的设置回收和结束等等的简单并发的机制