Linux10:多线程编程(线程与进程的区别,线程基本函数,线程通信,同步与互斥)

多线程编程(线程与进程的区别,线程基本函数,线程通信,同步与互斥)

1.线程与进程:

进程:
系统中程序执行和资源分配的基本单位
(数据段、代码段和堆栈段)

线程:
允许应用程序并发执行多个任务的机制
(轻量进程,进程内基本调度单位)
共享进程内存空间中并发的多道执行路径,共享进程的资源:
执行程序代码
数据段:
初始化数据段
未初始化数据段
堆空间
栈空间(独立)
文件描述符
遵循posix线程接口,称为pthread
需要头pthread.h文件,链接libpthread.a
pthread不是linux系统的库

进程–>线程:
linux2.2内核fork创建轻进程,最多4096,2.4 内核消除个数的限制

线程与进程的区别:
进程有独立的地址空间,线程没有单独的地址空间。(同一进程内的线程共享进程的地址空间)

为什么要引入线程:

启动一个进程所花费的空间远远大于启动一个线程所花费的空间(30倍左右),而且,线程间彼此切换所需的时间也远远小于进程间切换所需要的时间(>10倍)
维护进程对内核的消耗远远大于线程------ 进程表
线程间可以共享数据,更容易通信

进程VS线程:

在这里插入图片描述

线程和进程如何选择:

1)需要频繁创建销毁的优先用线程
2)需要进行大量计算的优先使用线程
考虑切换时费时费力,影响计算效率
3)强相关的处理用线程,弱相关的处理用进程
4)可能要扩展到多机分布的用进程,多核分布的用线程
5)都满足需求的情况下,用你最熟悉、最拿手的方式

2.线程基本函数:

2.1:线程的创建:

创建:

int pthread_create(pthread_t * thread,
const pthread_attr_t * attr,
void *(*start_routine)(void*), void *arg);

restrict是c99标准引入的,它只可以用于限定和约束指针,并表明指针是访问一个数据对象的唯一且初始的方式.即它告诉编译器,所有修改该指针所指向内存中内容的操作都必须通过该指针来修改,而不能通过其它途径(其它变量或指针)来修改;这样做的好处是,能帮助编译器进行更好的优化代码,生成更有效率的汇编代码.如 int *restrict ptr, ptr 指向的内存单元只能被 ptr 访问到,任何同样指向这个内存单元的其他指针都是未定义的,直白点就是无效指针。restrict 的出现是因为 C 语言本身固有的缺陷,C 程序员应当主动地规避这个缺陷,而编译器也会很配合地优化你的代码.
参数:

线程ID //unsigned long(pthread_t)

线程属性: // NULL

绑定属性:
绑定
一个用户线程固定地分配给一个内核线程,CPU 时间片的调度是面向内核线程(也就是轻量级进程),具有绑定属性的线程可以保证在需要的时候总有一个内核线程与之对应
非绑定
用户线程和内核线程关系不始终固定,由系统来控制分配

分离属性:
用来决定一个线程以什么样的方式来终止自己
分离
一个线程结束时立即释放它所占有的系统资源
非分离
线程结束占用的系统资源没有被释放,也就是没有真正的终止。只有当 pthread_join()函数返回时,创建的线程才能释放自己占用的系统资源

线程入口函数:
传递给线程入口函数的参数

返回值:
成功返回0,失败返回错误码

2.2:线程的标识:

thread_t pthread_self(void);
获取线程ID
unsigned long int --%lu

2.3:线程的退出:

void pthread_exit(void *value_ptr);
线程退出
正常退出
非正常退出
参数:
保存线程退出的状态

2.4:线程的等待(连接):

int pthread_join(pthread_t thread, void **value_ptr);
等待指定的线程退出
参数
要等待的线程ID
保存线程退出的状态 一般指定NULL
接收pthread_exit或者return的值

2.6:线程的取消:

int pthread_cancel(pthread_t thread);
取消一个还没有运行完的线程

2.7:线程的属性:

初始化:
int pthread_attr_init(pthread_attr_t *attr);
attr:线程属性
成功:0出错:-1
设置绑定属性:
int pthread_attr_setscope(pthread_attr_t *attr, int scope);
PTHREAD_SCOPE_SYSTEM:绑定
PTHREAD_SCOPE_PROCESS:非绑定
成功:0出错:-1
设置线程分离属性:
int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate);
PTHREAD_CREATE_DETACHED:分离
PTHREAD _CREATE_JOINABLE:非分离
成功:0出错:-1

2.8:线程的清理:

资源释放:

 void pthread_cleanup_push(void (*routine)(void*), void *arg);
  void pthread_cleanup_pop(int execute);

参数:

routine:注册清理的函数的指针
arg:传递给清理函数的参数
execute:决定这个清理函数是否被调用
0–不调用
非0–调用

注意:采用先入后出的栈结构管理,两个函数之间的程序段中的终止动作(包括调用pthread_exit()和异常终止(其他进程使用pthread_cancel取消当前进程) 不包含return)都将执行

3:线程间的通信:

1.直接共享进程的资源(利用全局变量)
2.信号(同进程):
pthread_kill();-------- kill

4:同步和互斥:

4.1:为什么要进行同步以及互斥:

实现对资源的保护、独占

4.2:同步和互斥的操作:

4.2.1:互斥信号量(信号锁):

安装所需要的库:

apt-get install manpages-posix-dev

含义:
互斥锁看作某种意义上的全局变量
每次操作资源的时候,默认去检测锁,如果锁处于加锁状态等待锁解开
如果锁处于解锁状态,加锁使用,使用完毕后,解锁。
分类:
快速互斥锁
最常用的锁,符合以上的含义
检测锁
快速互斥锁的非阻塞版本
递归锁
多次加锁
使用流程:

互斥锁初始化:pthread_mutex_init
互斥锁上锁:pthread_mutex_lock
互斥锁判断上锁:pthread_mutex_trylock
互斥锁解锁:pthread_mutex_unlock
消除互斥锁:pthread_mutex_destroy

互斥锁 API:
创建:
动态创建:
pthread_mutex_init
参数1:描述锁的变量 pthread_mutex_t mutex;

参数2:指定锁的类型:
默认NULL表示创建快速互斥锁
PTHREAD_MUTEX_INITIALIZER:创建快速互斥锁
函数传入值 Mutexattr PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP:创建递归互斥锁
PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP:创建检错互斥锁

静态创建:

pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
//静态创建快速互斥锁

加锁
int pthread_mutex_lock(pthread_mutex_t *mutex);//成功就加锁成功不成功就等待
int pthread_mutex_trylock(pthread_mutex_t *mutex);
成功就加锁,不成功就返回错误----非阻塞
解锁
int pthread_mutex_unlock(pthread_mutex_t *mutex);
销毁锁
int pthread_mutex_destroy(pthread_mutex_t *mutex);
小缺陷
小心死锁:在同一个线程中,第一次lock,没问题,然后没有unlock,又lock一次,会阻塞,且一直阻塞下去

4.2.2:条件变量:

含义:
当一个条件没有满足会阻塞程序,满足条件够可以解除阻塞
条件变量相关 API:
创建:
静态方式

pthread_cond_t cond = PTHREAD_COND_INITIALIZER;

动态方式

int pthread_cond_init(pthread_cond_t *restrict cond,
const pthread_condattr_t *restrict attr);
  没有满足条件阻塞
int pthread_cond_wait(pthread_cond_t *restrict cond,
              pthread_mutex_t *restrict mutex);
 满足条件解除阻塞
int pthread_cond_signal(pthread_cond_t *cond);

销毁

  int pthread_cond_destroy(pthread_cond_t *cond);

4.2.3:信号量(信号灯):

含义:
线程的信号量,与进程中的信号不同
本质上是一个非负的整数计数器,用来控制对公共资源的访问
原理:
P 操作使 sem 减一,V 操作使sem 加一。信号量sem 的值大于等于零时,进程(或线程)具有公共资源的访问权限----互斥 同步
信号量相关API:
创建:

int sem_init(sem_t *sem, int pshared, unsigned int value);

创建信号量sem,初始值为value pshared:指定为0
pshared:决定信号量能否在几个进程间共享。由于目前 Linux 还没有实现进程间共享信号量,所以这个值只能够取 0
获取信号量的值:

int sem_getvalue(sem_t *sem, int *sval);

消耗 -1:

 int sem_wait(sem_t *sem);---阻塞
 int sem_trywait(sem_t *sem);--非阻塞
 释放 +1:
 int sem_post(sem_t *sem);

清理:

 sem_destroy(sem_t *sem);

注意事项:

如果使用同步和互斥一定所有的线程都要遵循这个操作,否则就没有意义

猜你喜欢

转载自blog.csdn.net/weixin_40734514/article/details/109254157