线程的基础
进程和程序
进程是资源分配的基本单位
线程是执行的基本单位
进程和线程的关系
一个进程中可以有多个线程,最少有一个线程(主线程)
线程共享进程的资源,每个线程都有自己的属性。线程的id,线程自己的栈帧,自己的信号屏蔽字等。
进程的切换和进程的通信,消耗资源非常大,效率低下。
线程共享进程的资源,线程的切换和线程间的通信很灵活,消耗资源比较少,效率很好。
线程的创建,退出,分离,汇合
1.创建线程使用pthread_create(3)
#include <pthread.h>
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,\
void *(*start_routine) (void *), void *arg);
举例:验证 一个进程中有多个线程。需要获取到进程的pid和线程的tid。
获取进程的pid ,使用getpid(2)
获取线程的tid,使用pthread_self(3)
pthread_t pthread_self(void);
功能:获取线程自己的id
2.线程的退出
return 和exit(3)的区别
在线程函数中调用return只是线程执行函数的结束。代表线程的结束。
如果在线程函数中调用exit(3) 将会终止整个进程,那么进程中的线程就会终止。切记,不要在线程执行函数中调用exit(3)等函数。
终止一个线程使用pthread_exit(3)
#include <pthread.h>
void pthread_exit(void *retval);
功能:终止当前线程
#include <pthread.h>
int pthread_cancel(pthread_t thread);
线程的汇合和分离
线程创建以后,线程退出的时候,线程的资源自动回收,不需要主线程等待,这样的线程称为分离线程
线程创建以后,线程退出以后,需要其他线程回收线程的资源,这样的线程称为线程的汇合。
线程汇合使用pthread_join(3)完成。
#include <pthread.h>
int pthread_join(pthread_t thread, void **retval);
线程的分离使用pthread_detach(3)
#include <pthread.h>
int pthread_detach(pthread_t thread);
举例说明:退出,回合
补充:void *(*start_routine) (void *)
线程对共享资源的访问
线程创建完毕,进程中所有的线程是异步的。
要保证线程函数的安全性,那么线程必须为可重入的。
举例,线程函数不是可重入函数的时候,对临界资源的访问,造成程序的乱序。
线程同步
多个线程对临界资源进程访问的时候,需要由异步变为同步。
posix线程中提供了三种方式
1.mutex锁
pthread_mutex_init(3)
#include <pthread.h>
pthread_mutex_t 是mutex锁的类型 静态初始化一个mutex锁
pthread_mutex_t fastmutex = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t recmutex = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;
pthread_mutex_t errchkmutex = PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP;
int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutex‐ attr_t *mutexattr);
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);
使用mutex锁解决临界资源问题
2.条件变量
什么是条件变量?
线程间的同步:线程A需要等待某个条件成立才能继续往下执行,现在这个条件不成立,线程A就阻塞等待,而线程B在执行过程中,使这个条件成立,然后线程A继续往下之执行,那么这个条件就是条件变量。
pthread库中通过使用pthread_cont_t类型来表示条件变量类型。
对这种类型的操作包括一下:
pthread_cond_init(3)
#include <pthread.h>
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;//静态初始化条件变量
int pthread_cond_init(pthread_cond_t *cond, pthread_condattr_t *cond_attr);
int pthread_cond_signal(pthread_cond_t *cond);
int pthread_cond_broadcast(pthread_cond_t *cond);
int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);
int pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t
*mutex, const struct timespec *abstime);
int pthread_cond_destroy(pthread_cond_t *cond);
(1) 先解mutex锁
(2)等待signal的到来
(3)重新加mutex锁
生产者和消费者的问题
需要使用到链表。生产出来的节点串到链表的头部。消费者从链表的头部取走一个节点进行消费。
如果链表中没有节点的时候,消费者一直等待生产者生产。
3.信号量
对多个资源的共享使用信号量,想使用其中的一个资源的时候,首先判断是否有可用资源。如果有,是资源的可用数量减1.如果没有可用资源,线程等待其他线程释放资源。当线程释放资源的时候,资源可用数量加1.
信号量是一个类型 sem_t
关于信号量的操作,系统提供了一下函数 sem_init(3)
#include <semaphore.h>
int sem_init(sem_t *sem, int pshared, unsigned int value);
0 成功 -1 错误 errno被设置
sem_destroy(3)
#include <semaphore.h>
int sem_destroy(sem_t *sem);
sem_post(3)
#include <semaphore.h>
int sem_post(sem_t *sem);
sem_wait(3)
#include <semaphore.h>
int sem_wait(sem_t *sem);
int sem_trywait(sem_t *sem);
int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout);
举例使用信号量完成生产者和消费者模型
使用循环队列完成,队列固定大小