优点:线程间很容易进行通信,通过全局变量实现数据共享和交换
缺点:多个线程同时访问共享对象时需要引入同步和互斥机制
线程间通信借助全局变量
同步和互斥机制
信号量:pv操作
p:申请资源 资源量-1
v:释放资源 资源量 +1
sem_t sem;//定义一个资源量
sem_init(&sem,0,资源量)//初始化信号量(资源)-资源量
sem_wait (&sem)//获取资源量,阻塞等待
sem_post(&sem)//释放资源量
互斥锁:互斥
pthread_mutex_t mutex;//定义一把锁
pthread_mutex_init(&mutex,NULL)//初始化互斥锁
pthread_mutex_lock(&mutex)//上锁
pthread_mutex_unlock(&mutex)//解锁
pthread_mutex_destroy(&mutex)//销毁锁
条件变量:结合互斥锁
pthread_cond_init(&cond,NULL);
pthread_cond_wait(&cond,&mutex);//解锁,阻塞,满足条件解锁阻塞并上锁
pthread_cond_signal(&cond);//唤醒一个阻塞在pthread_cond_wait的线程
pthread_cond_broadcast(&cond);//唤醒所有阻塞在pthread_cond_wait的线程
pthread_cond_destroy(&cond);//销毁条件变量
同步(synchronization)指的是多个任务(线程)按照约定的顺序相互配合完成一件事情
1)信号量代表某一类资源,其值表示系统中该资源的数量
2)信号量是一个dd受保护的变量,只能通过三种操作来访问
初始化 sem_init
P操作(申请资源) sem_wait
V操作(释放资源) sem_post
3)信号量的值为非负整数 (>=0)
P(S) 含义如下:
if(信号量的值大于0)
{
申请资源的任务继续运行;
信号量的值减一;
}
else
{
申请资源的任务阻塞;
}
V(S) 含义如下:
if(没有任务在等待该资源)
{
信号量的值加一;
}
else
{
唤醒第一个等待的任务,让其继续运行
}
信号量:通过信号量实现同步操作;由信号量来决定线程是继续运行还是阻塞等待
信号量代表某一类资源,其值表示系统中该资源的数量,信号量值>0,表示有资源可以用,可以申请到资源,继续执行程序,信号量值<=0,表示没有资源可以用,无法申请到资源,阻塞。
信号量是一个受保护的变量,只能通过三种操作来访问:初始化sem_init、P操作(申请资源)sem_wait、V操作(释放资源)sem_post
谁要等待谁去完成:
#include
int sem_init(sem_t *sem, int pshared, unsigned int value);
功能:初始化信号量
参数:sem:初始化的信号量对象
pshared:信号量共享的范围(0: 线程间使用 非0:1进程间使用)
value:信号量初值 >=0 (资源量)
返回值:成功 0
失败 -1
#include
int sem_wait(sem_t *sem)
功能:申请资源 P操作 //申请一次资源 ,资源量减1
参数:sem:信号量对象
返回值:成功 0
失败 -1
#include
int sem_post(sem_t *sem)
功能:释放资源 V操作//释放一次资源 ,资源量加1
参数:sem:信号量对象
返回值:成功 0
失败 -1
#include
#include
#include
#include
#include
//在临界区上下位置加申请资源释放资源函数.
/*信号量:通过信号量实现同步操作;由信号量来决定线程是继续运行还是阻塞等待信号量代表某一类资源,
其值表示系统中该资源的数量,信号量值>0,表示有资源可以用,可以申请到资源,继续执行程序,
信号量值<=0,表示没有资源可以用,无法申请到资源,阻塞。
*/
//公用数据称为:临界资源
char buf[32] = "hello world"; //借助全局变量进行线程间的通信
sem_t sem,sem1;//定义一个资源量
//创建的线程
void *mythread(void *arg)
{
char *head = NULL;
char *tail = NULL;
char c;
while (1)
{
//操纵临界资源的代码,叫做临界区
sem_wait(&sem);
head = buf;
tail = buf + strlen(buf) - 1;
while (head < tail)
{
c = *head;
*head = *tail;
*tail = c;
head++;
tail--;
}
sem_post(&sem1);
//
}
// pthread_exit(NULL);
}
void *twothread(void *arg)
{
while (1)
{
sem_wait(&sem1);
//操纵临界资源的代码,叫做临界区
printf("%s\n", buf);
//
sem_post(&sem);
}
// pthread_exit(NULL);
}
int main(int argc, char const *argv[])
{
//主线程
pthread_t tid, tid1;
sem_init(&sem,0,1);
sem_init(&sem1,0,0);
pthread_create(&tid, NULL, mythread, NULL);
pthread_create(&tid1, NULL, twothread, NULL);
pthread_join(tid1, NULL);
pthread_join(tid, NULL);
return 0;
}
引入互斥(mutual exclusion)锁的目的是用来保证共享数据操作的完整性。互斥锁主要用来保护临界资源每个临界资源都由一个互斥锁来保护,某一资源同时只允许一个线程对其进行访问。线程必须先获得互斥锁才能访问临界资源,访问完资源后释放该锁。如果无法获得锁,线程会阻塞直到获得锁为止
#include
int pthread_mutex_init(pthread_mutex_t *mutex,pthread_mutexattr_t *attr)
功能:初始化互斥锁
参数:mutex:互斥锁
attr: 互斥锁属性 // NULL表示缺省属性
返回值:成功 0
失败 -1
宏初始化:
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
#include
int pthread_mutex_destroy(pthread_mutex_t *mutex)
功能:销毁互斥锁
参数:mutex:互斥锁
#include
int pthread_mutex_lock(pthread_mutex_t *mutex)
功能:申请互斥锁,调用这个函数的线程阻塞,直到mutex释放为止
参数:mutex:互斥锁
返回值:成功 0
失败 -1
#include
int pthread_mutex_unlock(pthread_mutex_t *mutex)
功能:释放互斥锁
参数:mutex:互斥锁
返回值:成功 0
失败 -1
死锁是指多个进程因竞争资源而造成的一种僵局(互相等待),若无外力作用,这些进程都将无法向前推进。
例如,在某一个计算机系统中只有一台打印机和一台输入 设备,进程P1正占用输入设备,同时又提出使用打 印机的请求,但此时打印机正被进程P2 所占用,而P2在未释放打印机之前,又提出请求使用正被P1占用着输入设备。这样两个进程相互无休止地等待下去,均无法继续执行,此时两个进程陷入死锁状态。
1. 系统资源的竞争
系统资源的竞争导致系统资源不足,以及资源分配不当,导致死锁。
2. 进程运行推进顺序不合适
进程在运行过程中,请求和释放资源的顺序不当,会导致死锁。
1.互斥条件:一个资源每次只能被一个进程使用,即在一段时间内某 资源仅为一个进程所占有。此时若有其他进程请求该资源,则请求进程只能等待。
2.请求与保持条件:进程已经保持了至少一个资源,但又提出了新的资源请求,而该资源已被其他进程占有,此时请求进程被阻塞,但对自己已获得的资源保持不放。
3.不可剥夺条件:进程所获得的资源在未使用完毕之前,不能被其他进程强行夺走,即只能 由获得该资源的进程自己来释放(只能是主动释放)。
4.循环等待条件: 若干进程间形成首尾相接循环等待资源的关系
我们可以通过破坏死锁产生的4个必要条件来 预防死锁,由于资源互斥是资源使用的固有特性是无法改变的。
1.破坏“不可剥夺”条件:一个进程不能获得所需要的全部资源时便处于等待状态,等待期间他占有的资源将被隐式的释放重新加入到 系统的资源列表中,可以被其他的进程使用,而等待的进程只有重新获得自己原有的资源以及新申请的资源才可以重新启动,执行。
2.破坏”请求与保持条件“:第一种方法静态分配即每个进程在开始执行时就申请他所需要的全部资源。第二种是动态分配即每个进程在申请所需要的资源时他本身不占用系统资源。
3.破坏“循环等待”条件:采用资源有序分配其基本思想是将系统中的所有资源顺序编号,将紧缺的,稀少的采用较大的编号,在申请资源时必须按照编号的顺序进行,一个进程只有获得较小编号的进程才能申是指两个或两个以上的进程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去
死锁产生的四个必要条件:
1、互斥使用,即当资源被一个线程使用(占有)时,别的线程不能使用
2、不可抢占,资源请求者不能强制从资源占有者手中夺取资源,资源只能由资源占有者主动释放。
3、请求和保持,即当资源请求者在请求其他的资源的同时保持对原有资源的占有。
4、循环等待,即存在一个等待队列:P1占有P2的资源,P2占有P3的资源,P3占有P1的资源。这样就形成了一个等待环路。
注意:当上述四个条件都成立的时候,便形成死锁。当然,死锁的情况下如果打破上述任何一个条件,便可让死锁消失。
1, 2 ,3
A : 需要1,占有2
B :需要2,占有3
C:需要3,占有1
#include
#include
#include
#include
#include
pthread_mutex_t mutex1,mutex2;
//定义两个互斥锁,互斥锁主要用来保护临界资源每个临界资源都由一个互斥锁来保护,某一资源同时只允许一个线程对其进行访问。
/*同时两个互斥锁也可以实现同步操作 */
//公用数据称为:临界资源
char buf[32] = "hello world"; //借助全局变量进行线程间的通信
//创建的线程
void *mythread(void *arg)
{
char *head = NULL;
char *tail = NULL;
char c;
while (1)
{
//操纵临界资源的代码,叫做临界区
pthread_mutex_lock(&mutex1);//关闭互斥锁1,先运行线程1
head = buf;
tail = buf + strlen(buf) - 1;
while (head < tail)
{
c = *head;
*head = *tail;
*tail = c;
head++;
tail--;
}
pthread_mutex_unlock(&mutex2);//释放互斥锁2,阻塞线程2运行
//
}
// pthread_exit(NULL);
}
void *twothread(void *arg)
{
while (1)
{
pthread_mutex_lock(&mutex2);//关闭互斥锁2,先运行线程2
//操纵临界资源的代码,叫做临界区
sleep(1);
printf("%s\n", buf);
//
pthread_mutex_unlock(&mutex1);//释放互斥锁1,阻塞线程1运行
}
// pthread_exit(NULL);
}
int main(int argc, char const *argv[])
{
//主线程
pthread_t tid, tid1;
pthread_mutex_init(&mutex1,NULL);//初始化互斥锁
pthread_mutex_init(&mutex2,NULL);
pthread_mutex_lock(&mutex2);//关闭互斥锁2
pthread_create(&tid, NULL, mythread, NULL);
pthread_create(&tid1, NULL, twothread, NULL);
pthread_join(tid1, NULL);
pthread_join(tid, NULL);
return 0;
}
一般与互斥锁搭配使用,可以实现同步机制
1)pthread_cond_init:初始化
2)pthread_cond_wait:阻塞等待条件产生,没有条件产生时阻塞,同时解锁,当条件产生时结束阻塞,再次上锁
3)pthread_mutex_lock(); //上锁
4)pthread_cond_wait(cond, lock); //如果没有条件产生时,解锁,当等待到条件产生时,上锁
5)pthread_cond_signal:产生条件,不阻塞
6)pthread_cond_wait先执行,pthread_cond_signal再产生条件
生产者与消费者:先有消费者再有生产者
消费者--》需求--》生产者--》生产--》消费者
pthread_cond_wait:消费者
pthread_cond_signal:生产者
#intclude
int pthread_cond_init(pthread_cond_t *restrict cond,const pthread_condattr_t *restrict attr);
功能:初始化条件变量
参数:cond:是一个指向结构pthread_cond_t的指针(条件变量地址)
restrict attr:是一个指向结构pthread_condattr_t的指针,一般设为NULL
返回值:成功:0 失败:非0
声明后初始化:pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
#intclude
int pthread_cond_wait(pthread_cond_t *restrict cond,pthread_mutex_t *restrict mutex);
功能:无条件等待信号的产生,当线程调用这个函数的时候,当前线程就会被加入到条件变量中,并阻塞等待。
参数:restrict cond:要等待的条件(要加入的条件变量地址)
restrict mutex:对应的锁
返回值:成功:0,失败:不为0
1.解锁
2.阻塞线程
3.唤醒时,上锁
#intclude
int pthread_cond_signal(pthread_cond_t *cond);
功能:给条件变量发送信号,加入到条件变量中的线程会被挂起,这个函数被调用的时候 让条件变量(等待队列)中排在队首的线程取消挂起,其实就是唤醒排在队首的线 程。唤醒一个阻塞在pthread_cond_wait函数上的线程
参数:cond:条件变量值
返回值:成功:0,失败:非0
int pthread_cond_destroy(pthread_cond_t *cond);
功能:将条件变量销毁
参数:cond:条件变量值(要销毁的条件变量地址)
返回值:成功:0, 失败:非0
int pthread_cond_broadcast(pthread_cond_t *cond);
功能: 唤醒全部阻塞在条件变量上的线程
参数: @cond 条件变量地址
返回值: 成功: 0
失败: errno值
//练习:三个线程分别输出A、B、C,将三个线程实现同步,顺序输出ABCABC……
#include
#include
#include
#include
#include
#include
pthread_cond_t cond; //声明一个条件变量
pthread_mutex_t mutex; //声明一个锁
int flags = 1; //条件
void *thread1(void *arg)
{
while (1)
{
pthread_mutex_lock(&mutex);
while (flags != 1)
{
pthread_cond_wait(&cond, &mutex);
//解锁 阻塞cond 条件产生解除阻塞,上锁
}
printf("A");
flags = 2;
pthread_cond_broadcast(&cond); //唤醒全部阻塞在条件变量上的线程
pthread_mutex_unlock(&mutex);
}
return NULL;
}
void *thread2(void *arg)
{
while (1)
{
pthread_mutex_lock(&mutex);
while (flags != 2)
pthread_cond_wait(&cond, &mutex);
//解锁 阻塞cond 条件产生解除阻塞,上锁
printf("B");
flags = 3;
pthread_cond_broadcast(&cond); //唤醒全部阻塞在条件变量上的线程
pthread_mutex_unlock(&mutex);
}
return NULL;
}
void *thread3(void *arg)
{
while (1)
{
pthread_mutex_lock(&mutex);
while (flags != 3)
pthread_cond_wait(&cond, &mutex);
//解锁 阻塞cond 条件产生解除阻塞,上锁
printf("C");
flags = 4;
pthread_cond_broadcast(&cond); //唤醒全部阻塞在条件变量上的线程
pthread_mutex_unlock(&mutex);
}
return NULL;
}
void *thread4(void *arg)
{
while (1)
{
pthread_mutex_lock(&mutex);
while (flags != 4)
pthread_cond_wait(&cond, &mutex);
//解锁 阻塞cond 条件产生解除阻塞,上锁
printf("D\t");
fflush(NULL);
flags = 1;
pthread_cond_broadcast(&cond); //唤醒全部阻塞在条件变量上的线程
pthread_mutex_unlock(&mutex);
}
return NULL;
}
int main(int argc, char const *argv[])
{
pthread_t tid1, tid2, tid3, tid4;
//初始化条件变量
pthread_cond_init(&cond, NULL);
//初始化互斥锁
pthread_mutex_init(&mutex, NULL);
pthread_create(&tid1, NULL, thread1, NULL);
pthread_create(&tid2, NULL, thread2, NULL);
pthread_create(&tid3, NULL, thread3, NULL);
pthread_create(&tid4, NULL, thread4, NULL);
pthread_join(tid1, NULL);
pthread_join(tid2, NULL);
pthread_join(tid3, NULL);
return 0;
}