同步与互斥这两个概念经常被混淆,所以在这里说一下它们的区别。

一、同步与互斥的区别

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

总结:

  1. 互斥是指某一资源同时只允许一个访问者对其进行访问,具有唯一性和排它性。但互斥无法限制访问者对资源的访问顺序,即访问是无序的。
  2. 同步是指在互斥的基础上(大多数情况),通过其它机制实现访问者对资源的有序访问。
  3. 同步其实已经实现了互斥,所以同步是一种更为复杂的互斥。
  4. 互斥是一种特殊的同步。

个人站点:http://songlee24.github.com

【Linux多线程】同步与互斥的区别的更多相关文章

  1. Linux多线程同步方式

    当多个线程共享相同的内存时,需要确保每个线程看到一致的数据视图,当多个线程同时去修改这片内存时,就可能出现偏差,得到与预期不符合的值.为啥需要同步,一件事情逻辑上一定是有序的,即使在并发环境下:而操作 ...

  2. linux多线程同步pthread_cond_XXX条件变量的理解

    在linux多线程编程中,线程的执行顺序是不可预知的,但是有时候由于某些需求,需要多个线程在启动时按照一定的顺序执行,虽然可以使用一些比较简陋的做法,例如:如果有3个线程 ABC,要求执行顺序是A-- ...

  3. linux多线程同步的四种方式

    1. 在并发情况下,指令执行的先后顺序由内核决定.同一个线程内部,指令按照先后顺序执行,但不同线程之间的指令很难说清楚是哪一个先执行.如果运行的结果依赖于多线程执行的顺序,那么就会形成竞争条件,每次运 ...

  4. Windows下C++多线程同步与互斥简单运用

    1.  互斥量,Mutex #include <Windows.h> #include <iostream> using namespace std; DWORD WINAPI ...

  5. Windows下C++多线程同步与互斥简单运用(转)

    1.  互斥量,Mutex #include <Windows.h> #include <iostream> using namespace std; DWORD WINAPI ...

  6. Linux多线程同步机制

    http://blog.163.com/he_junwei/blog/static/19793764620141711130253/ http://blog.csdn.net/h_armony/art ...

  7. JAVA 多线程同步与互斥

    1. 为什么需要互斥: ​互斥操作  保证了  多线程操作的  原子性 , java的 互斥 语义 有 synchronized 关键字 提供. 主要方式 有  同步代码块 和  同步方法 两种 2. ...

  8. linux线程同步(1)-互斥量

    一.概述                                                   互斥量是线程同步的一种机制,用来保护多线程的共享资源.同一时刻,只允许一个线程对临界区进行 ...

  9. linux多线程编程之互斥锁

    多线程并行运行,共享同一种互斥资源时,需要上互斥锁来运行,主要是用到pthread_mutex_lock函数和pthread_mutex_unlock函数对线程进行上锁和解锁 下面是一个例子: #in ...

随机推荐

  1. TYPE=MyISAM 与 ENGINE=MyISAM 的区别(摘要版)

    TYPE=MyISAM 和 ENGINE=MyISAM 都是设置数据库存储引擎的语句 (老版本的MySQL使用TYPE而不是ENGINE(例如,TYPE = MYISAM). MySQL 5.1为向下 ...

  2. Python框架Django的入门

    本篇文章主要给大家介绍Django的入门知识:

  3. Eclipse使用入门指南及技巧

    Java是必须的   安装一个JDK就可以了,比如jdk-6u39-windows-x64.exe,安装完毕,会自行安装JRE. 如果不用IDE,这个时候用记事本也是可以写程序,然后用javac编译, ...

  4. flask中模板日期格式控制和jinja2中模板格式控制

    1.flask的模块语言采用的是jinja2,首先说一下jinja2中的模板格式控制的使用,即模板自定义filters的使用. python的脚本如下(采用的是格式显示日期): 1 from jinj ...

  5. js如何判断数组是Array类型

    在说明如何判断一个对象为数组类型前,我们先巩固下js的数据类型,js一共有六大数据类型:number.string.object.Boolean.null.undefined.var str=&quo ...

  6. WC2007 石头剪刀布 数学+最小费用最大流

    题面: 有N个人参加一场比赛,赛程规定任意两个人之间都要进行一场比赛:这样总共有N*(N-1)/2场比赛.比赛已经进行了一部分,我们想知道在极端情况下,比赛结束后最多会发生多少剪刀石头布情况.即给出已 ...

  7. Buffer.allocUnsafe()

    Buffer.allocUnsafe(size) size {Number} 分配一个 size 字节大小的新的非零填充(non-zero-filled)的 Buffer.size 必须小于等于 re ...

  8. mysql常用命令用法

    Mysql帮助文档地址:http://dev.mysql.com/doc/ 1.创建数据库: create database database_name; 2.选择数据库: use database_ ...

  9. Python Pandas库的学习(一)

    今天我们来学习一下Pandas库,前面我们讲了Numpy库的学习 接下来我们学习一下比较重要的库Pandas库,这个库比Numpy库还重要 Pandas库是在Numpy库上进行了封装,相当于高级Num ...

  10. DataBase安装及简单配置

    下载以及安装 打开mysql官网下载页面:http://dev.mysql.com/downloads/mysql/ 用管理员身份打开cmd命令行工具,cd到解压文件的bin目录 输入mysqld i ...