概述
同步与互斥这两个概念经常被混淆,所以在这里说一下它们的区别。
一、同步与互斥的区别
1. 同步
同步,又称直接制约关系,是指多个线程(或进程)为了合作完成任务,必须严格按照规定的 某种先后次序来运行。
例如,线程 T2 中的语句 y 要使用线程 T1 中的语句 x 的运行结果,所以只有当语句 x 执行完成之后语句 y 才可以执行。我们可以使用信号量进行同步:
semaphore S=0; // 初始化信号量
T1() {
...
x; // 语句x
V(S); // 告诉线程T2,语句x已经完成
...
}
T2() {
...
P(S); // 检查语句x是否运行完成
y; // 检查无误,运行y语句
...
}
2. 互斥
互斥,又称间接制约关系,是指系统中的某些共享资源,一次只允许一个线程访问。当一个线程正在访问该临界资源时,其它线程必须等待。
例如,打印机就是一种临界资源,而访问打印机的代码片段称为临界区,故每次只允许一个线程进入临界区。—— 我们同样可以使用信号量解决互斥问题,只需把临界区置于 P(S) 和 V(S) 之间,即可实现两线程对临界资源的互斥访问。
semaphore S=1; // 初始化信号量
T1() {
...
P(S);
线程T1的临界区; // 访问临界资源
V(S);
...
}
T2() {
...
P(S);
线程T2的临界区; // 访问临界资源
V(S);
...
}
二、一个同步的例子
如下图,为了求出 1 到 n 的平均值,需要三个线程协调它们的工作次序来完成,这就是同步:
为了使多个线程按顺序正确执行,应设置若干个初始值为 0 的信号量:
#include<iostream>
#include<pthread.h>
#include"semaphore.hpp"
using namespace std;
int sem1, sem2;
int n = 10; /*1...n的平均值*/
int sum = 0;
double average = 0;
void* t1(void* arg)
{
for(int i=1; i<=n; ++i)
sum += i;
sem_v(sem1); /*V操作,通知t2求和已完成*/
}
void* t2(void* arg)
{
sem_p(sem1); /*P操作,等待t1完成*/
average = (double)sum/n;
sem_v(sem2); /*V操作,通知main求平均已完成*/
}
int main()
{
sem1 = creat_sem("/" , 0); /*创建信号量*/
sem2 = creat_sem("/home", 0);
pthread_t id[2];
pthread_create(&id[0], NULL, t1, NULL);
pthread_create(&id[1], NULL, t2, NULL);
sem_p(sem2); /*P操作,等待t2完成*/
cout << "The sum is: " << sum << endl;
cout << "The average is: " << average << endl;
del_sem(sem1); /*删除信号量*/
del_sem(sem2);
return 0;
}
下面是信号量的相关函数,详见《信号量》。
// semaphore.hpp
#include<cstdio>
#include<cstdlib>
#include<sys/sem.h>
// 联合体,用于semctl初始化
union semun
{
int val; /*for SETVAL*/
struct semid_ds *buf;
unsigned short *array;
};
// 初始化信号量
int init_sem(int sem_id, int value)
{
union semun tmp;
tmp.val = value;
if(semctl(sem_id, 0, SETVAL, tmp) == -1)
{
perror("Init Semaphore Error");
return -1;
}
return 0;
}
// P操作:
// 若信号量值为1,获取资源并将信号量值-1
// 若信号量值为0,进程挂起等待
int sem_p(int sem_id)
{
struct sembuf sbuf;
sbuf.sem_num = 0; /*序号*/
sbuf.sem_op = -1; /*P操作*/
sbuf.sem_flg = SEM_UNDO;
if(semop(sem_id, &sbuf, 1) == -1)
{
perror("P operation Error");
return -1;
}
return 0;
}
// V操作:
// 释放资源并将信号量值+1
// 如果有进程正在挂起等待,则唤醒它们
int sem_v(int sem_id)
{
struct sembuf sbuf;
sbuf.sem_num = 0; /*序号*/
sbuf.sem_op = 1; /*V操作*/
sbuf.sem_flg = SEM_UNDO;
if(semop(sem_id, &sbuf, 1) == -1)
{
perror("V operation Error");
return -1;
}
return 0;
}
// 删除信号量集
int del_sem(int sem_id)
{
union semun tmp;
if(semctl(sem_id, 0, IPC_RMID, tmp) == -1)
{
perror("Delete Semaphore Error");
return -1;
}
return 0;
}
// 创建信号量,返回其ID
int creat_sem(const char* path, int value)
{
int sem_id; /*信号量集ID*/
key_t key;
/*获取key值*/
if((key = ftok(path, 'z')) < 0)
{
perror("ftok error");
exit(1);
}
/*创建信号量集,其中只有一个信号量*/
if((sem_id = semget(key, 1, IPC_CREAT|0666)) == -1)
{
perror("semget error");
exit(1);
}
init_sem(sem_id, value);
return sem_id;
}
线程 t2 需要等待线程 t1 (求和)完成以后才能够执行;主线程 main 需要等待线程 t2 (求平均)完成以后才能够执行输出。编译运行结果如下:
$ g++ -lpthread -o synchronized synchronized.cpp
$ ./synchronized
The sum is: 55
The average is: 5.5
最后
以上就是昏睡月饼为你收集整理的Linux多线程编程--同步与互斥 一、同步与互斥的区别 二、一个同步的例子的全部内容,希望文章能够帮你解决Linux多线程编程--同步与互斥 一、同步与互斥的区别 二、一个同步的例子所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
发表评论 取消回复