一、线程
1、线程的概念
线程:是进程并发执行多个任务(线程)的机制,同进程下的多个线程共用同一进程资源。
进程:是由一个或多个线程组成的
串行:多个任务在单核CPU上按顺序执行
并发:多个任务在单核CPU上,以时间片轮机制执行
并行:多核CPU同时执行多个任务
2、进程和线程的区别
1. 进程之间用户空间独立,内核资源共享,,线程共享进程的资源
2. 进程是资源分配日的最小单位,线程是任务执行的最小单位
3. 多进程之间需要引入进程间通讯机制(IPC),多线程之间需要引入同步互斥
4. 进程的资源比线程多
5. 线程的效率比进程高
6. 进程的稳定性比线程强
3、线程的函数
1)安装线程的man手册
安装线程:sudo apt-get install manpages-posix*
注意如果出现锁的错误,解决方式:删除以下两个文件
rm /var/lib/dpkg/lock lock-frontend
2)pthread_create
格式:
#include <pthread.h>
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
void *(*start_routine) (void *), void *arg);
Compile and link with -pthread.
功能:在进程内创建一个新的线程
参数:
pthread_t *thread:该指针指向的内存中存储线程id
const pthread_attr_t *attr:线程的属性(线程的描述信息),写NULL,表示默认属性(结合态)
可以通过pthread_attr_init 修改属性,分离态
void *(*start_routine) (void *): 创建的线程函数名,该函数必须是函数指针
arg:表示线程体的参数,可以写NULL
返回值:
成功返回0
失败方error number,此时线程id未被定义
1. 分支线程结束,主线程不会结束
2. 主线程结束,分支线程也会结束
3. 分支线程和主线程都可以使用全剧变量
4. 分支线程使用主线程的局部变量
5. 主线程访问分支线程的局部变量
6. 主线程访问分支线程的变量的地址(使用分支线程的局部变量)
3)pthread_self(获取线程ID)
格式:
#include <pthread.h>
pthread_t pthread_self(void);
Compile and link with -pthread. ---> 编译的时候需要加 -pthread
功能:获取调用线程的线程id
返回值:返回调用者的线程id
4)pthread_exit(结束线程)
格式:
#include <pthread.h>
void pthread_exit(void *retval);
Compile and link with -pthread. ---> 编译的时候需要加 -pthread
功能:终止调用线程的,一般用于分支线程
参数:
void *retval: 线程的返回值,可以写NULL
5)pthread_ join(回收指定函数的资源)
格式:
#include <pthread.h>
int pthread_join(pthread_t thread, void **retval);
Compile and link with -pthread.
功能:阻塞函数,等待接收指定线程的资源,回收指定线程的资源
参数:
pthread_t thread:阻塞等待接收的指定线程id
void **retval:接收pthread_exit的退出状态值
需要指向一级指针的地址,不接收可以写NULL
返回值:
成功返回0,失败返回error number
6)pthread_detach(分离线程)
格式:
#include <pthread.h>
int pthread_detach(pthread_t thread);
Compile and link with -pthread.
功能:分离线程的,结束线程的资源有系统自动回收,不会阻塞
一旦使用pthread_detach函数,则pthread_join则无效,不会阻塞回收资源
参数:
pthread_t thread:分离的线程id
返回值:
成功返回0,失败返回error number
7)pthread_cancel(结束子线程)
格式:
#include <pthread.h>
int pthread_cancel(pthread_t thread);
Compile and link with -pthread.
功能:对指定子线程发送一个取消(结束)请求(信号),子进程可以选择取消的状态,以及是否结束线程
参数:
pthread_t thread:指定的子线程id
返回值:
成功返回0,失败返回error number
8)pthread_setcancelstate(选择是否同意主进程的请求)
格式:
#include <pthread.h>
int pthread_setcancelstate(int state, int *oldstate);
功能:设置取消的状态
参数:
int state:
PTHREAD_CANCEL_ENABLE,可以取消
PTHREAD_CANCEL_DISABLE,不可以取消
int *oldstate:获取上一次的取消状态值的,不想要获取写NULL
返回值:
成功返回0,失败返回非0的error number
9)pthread_setcanceltype(选择取消的状态)
格式:
int pthread_setcanceltype(int type, int *oldtype);
Compile and link with -pthread.
功能:设置取消类型
参数:
int type:
PTHREAD_CANCEL_DEFERRED:延时取消(遇到取消点:sleep,printf)
PTHREAD_CANCEL_ASYNCHRONOUS:立刻取消
int *oldtype:获取上一次的取消类型值的,不想要获取写NULL
返回值:
成功返回0,失败返回非0的error number
4、同步互斥
由于线程之间共享资源,不同线程在同时修改同一变量时,会出现数据混乱;
而同步互斥是解决数据混乱的方法。
1)同步互斥问题(如何完成同步互斥操作)
以两个分支线程为例
临界资源:是多个线程抢夺的资源共享资源(全局变量、内核资源、数据库)
临界区:是执行访问临界资源的代码
互斥:同一时间只允许一个线程访问临界资源,具备唯一性,排他性,不确定访问者的顺序
同步:在互斥的基础上,确定访问者的顺序
解决同步互斥问题的方式:同步锁、信号量(信号灯)、条件变量
2)互斥锁
原理
将需要访问临界资源的线程做上锁操作,如果上锁成功则执行代码,如果上锁失败,则等待。
1. pthread_mutex_init(互斥锁初始化)
格式:
#include <pthread.h>
方式1:
pthread_mutex_t fastmutex = PTHREAD_MUTEX_INITIAL_IZER;
方式2:
int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *mutexattr);
功能:初始化互斥锁
参数:
pthread_mutex_t *mutex:该指针指向的内存中存储你要初始化的互斥锁
const pthread_mutexattr_t *mutexattr:互斥锁的属性
写NULL,默认属性 默认用于线程
不写NULL,默认进程,或者可以通过 修改其他属性:pthread_mutexattr_init(递归锁,读写锁,自旋锁等)
返回值:
永远返回0
2. pthread_mutex_lock(上锁)
格式: int pthread_mutex_lock(pthread_mutex_t *mutex);
功能:上锁,阻塞函数,如果存在锁资源则上锁成功,如果没有锁资源则阻塞等待
参数:pthread_mutex_t *mutex该指针指向的内存中存储你要初始化的互斥锁
返回值:成功返回0,失败返回非0
3. pthread_mutex_unlock(关锁)
格式: int pthread_mutex_unlock(pthread_mutex_t *mutex);
功能:解锁
参数:pthread_mutex_t *mutex该指针指向的内存中存储你要初始化的互斥锁
返回值:成功返回0,失败返回非0
4. pthread_mutex_destroy(销毁互斥锁,释放内存)
格式:int pthread_mutex_destroy(pthread_mutex_t *mutex);
功能:销毁锁,释放锁资源
参数:pthread_mutex_t *mutex该指针指向的内存中存储你要初始化的互斥锁
返回值:成功返回0,失败返回非0
5. pthread_mutex_trylock(尝试上锁)
格式: int pthread_mutex_trylock(pthread_mutex_t *mutex);
功能:尝试上锁,非阻塞函数,
如果存在锁资源则上锁成功,如果没有锁资源则不会阻塞等待,直接向下运行
参数:pthread_mutex_t *mutex该指针指向的内存中存储你要初始化的互斥锁
返回值:成功返回0,失败返回非0
实列:
6. 生产者、消费者模型(互斥锁完成)
3)信号量(信号灯)
原理:
让访问临界资源的线程都去申请信号量,如果信号量的值大于0,则申请成功,信号量的值减1;
如果信号量的值等于0,则申请失败,阻塞直到信号量的值大于0,解除阻塞,重新申请。
1. sem_init(初始化信号量)
格式 #include <semaphore.h>
int sem_init(sem_t *sem, int pshared, unsigned int value);
Link with -pthread.
功能:初始化信号量的
参数:
sem_t *sem:该指针中存储信号量
int pshared:
0:用于线程
非0:用于进程
unsigned int value:最多允许几个线程同时访问临界资源
返回值:成功返回0,失败返回-1. 跟新errno
2. sem_wait(申请信号量)
格式 #include <semaphore.h>
int sem_wait(sem_t *sem);
功能:阻塞函数,申请信号量的 P操作,减1
如果信号量的值大于0,申请成功,值减1
如果信号量的值等于,则申请失败,阻塞直到信号量的值大于0
参数:sem_t *sem:该指针中存储信号量
返回值:成功返回0,失败返回-1. 跟新errno
3. sem_post(释放信号量)
格式 #include <semaphore.h>
int sem_post(sem_t *sem);
Link with -pthread.
功能:释放信号量的 V操作,信号量的值加1
参数:sem_t *sem:该指针中存储信号量
返回值:成功返回0,失败返回-1. 跟新errno
4. sem_destroy(销毁信号量,释放内存)
格式 #include <semaphore.h>
int sem_destroy(sem_t *sem);
Link with -pthread.
功能:销毁信号量,释放资源
参数:sem_t *sem:该指针中存储信号量
返回值:成功返回0,失败返回-1. 跟新errno
5. sem_getvalue(访问指定信号量的,还有几个线程可以进程)
#include <semaphore.h>
int sem_getvalue(sem_t *sem, int *sval);
Link with -pthread.
功能:获取指定信号量还可以允许几个线程访问
参数:
sem_t *sem:该指针中存储信号量
int *sval:允许几个线程
返回值:成功返回0,失败返回-1. 跟新errno
6. 生产者、消费者模型(信号量完成)
7. 使用信号量完成多线程写入日志
4)条件变量
原理:
把不需要访问临界资源的线程休眠,并设置一个唤醒条件,这个条件称为条件变量,
当需要访问临界资源时,通过其他线程唤醒。
1. pthread_cond_init(初始化)
格式:
方式1:
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
方式2:
int pthread_cond_init(pthread_cond_t *cond, pthread_con‐
dattr_t *cond_attr);
功能:初始化条件变量
参数:
pthread_cond_t *cond:该指针指向的内存中存储条件变量
pthread_cond_attr_t *cond_attr:条件变量的属性
NULL:默认属性表示线程
非NULL,表示进程
返回值:
成功返回0,失败返回非0
2. pthread_cond_wait(睡眠)
格式:
int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);
功能:阻塞函数,解锁,如果其他函数唤醒休眠函数,则上锁的
参数:
pthread_cond_t *cond:该指针指向的内存中存储条件变量
pthread_mutex_t *mutex:互斥锁
返回值:
成功返回0,失败返回非0
1.为什么条件中会有锁呢
当多个CPU执行多线程时,例如两个CPU分别同时执行AB线程 ,且AB线程中同时调用
pthread_cond_wait函数,两个线程需要添加到内核的休眠队列中,
此时需要使用互斥锁,避免AB线程存放内核休眠队列出现竞态
2.pthread_cond_wait函数执行流程
1.pthread_mutex_lock
2.pthread_cond_wait休眠,解锁
3.假设其他线程唤醒该线程
尝试上锁,如果上锁失败,则继续休眠,唤醒失败
上锁成功,则唤醒成功
4.解锁 pthread_mutex_unlock
3.该函数还有可能被被内核发送的某个信号意外唤醒(内核优化)
3. pthread_cond_signal(逐个唤醒)
格式:int pthread_cond_signal(pthread_cond_t *cond);
功能:唤醒休眠队列中的第一个线程
参数:pthread_cond_t *cond:该指针指向的内存中存储条件变量
返回值:
成功返回0,失败返回非0
4. pthread_cond_broadcast(全部唤醒)
格式: int pthread_cond_broadcast(pthread_cond_t *cond);
功能:唤醒休眠队列中的多有线程
参数:pthread_cond_t *cond:该指针指向的内存中存储条件变量
返回值:
成功返回0,失败返回非0
5. pthread_cond_destroy(条件变量)
格式: int pthread_cond_destroy(pthread_cond_t *cond);
功能:销毁信号量,释放资源
参数:pthread_cond_t *cond:该指针指向的内存中存储条件变量
返回值:
成功返回0,失败返回非0
6. 条件变量的使用
作业:
1. 创建一个分支线程,在主线程中拷贝文件的前一部分,分支线程拷贝文件的后一部分
2. 解读代码
info 1 from child procsee_1
info 1 from child procsee
3. 解读代码(输出8次)