同步问题总结


这次写点简单的东西,记得最开始接触“同步”这个词是在大三去旁听操作系统课时候,当时感觉还是很难理解的。今天正巧看《UNIX环境高级编程》时候又看到它了,就好好做一次总结吧。

1. 为什么需要同步?

上面的图是从《高级编程》中截的图,虽然它是针对线程的,但是这里要说明,不仅仅线程要考虑这个问题,只要涉及到并发的程序,都要考虑同步。比如多进程共享内存,比如某个驱动会同时被打开,而且会被几个进程同时修改驱动中的值或者寄存器......

原理上都是一样的,多线程并发访问是一定要注意的,因为同一进程的多个线程本身就共享进程资源或者说变量的内存。就拿上图来说,我们对i变量的值+1操作,那么这个简简单单的+1操作真正到了CPU上会怎么执行呢?通常分为3步:

(1) 从内存单元读入寄存器

(2) 在寄存器上进行变量值增加

(3) 把新的值写回内存单元

这就导致了上图的问题,A线程在把i从内存读入寄存器改变过程中(还没写回到内存),B线程也对i做了同样操作,以至于最后结果就是读入的都是5,写入的都是6,那么本来我们是要对i增加2次的,实际却增加了1次。这种操作时间问题可能发生在ns级别,但是以当今处理器动辄几GB的速度来说,发生这种情况概率还是很大的。

2.验证试验

下面我们就做实验来实际看看这种情况。

看下程序:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>

#define NUM 40000000

pthread_t tid1;
pthread_t tid2;

unsigned int count1 = 0;
unsigned int count2 = 0;
unsigned int count = 0;

void * thr_fn1(void *arg)
{
	while(count1<NUM)
	{
		count++;
		count1++;
	}
}

void * thr_fn2(void *arg)
{
	while(count2<NUM)
	{
		count++;
		count2++;
	}
}

int main(void)
{
	int		err;

	err = pthread_create(&tid1, NULL, thr_fn1, NULL);
	if (err != 0)
		perror("can't create thread1");

	err = pthread_create(&tid2, NULL, thr_fn2, NULL);
	if (err != 0)
		perror("can't create thread2");

	pthread_join(tid1, NULL);
	pthread_join(tid2, NULL);

	printf("count = %u, count1 = %u, count2 = %u\n", count, count1, count2);
	exit(0);
}

程序很简单,就是创建两个线程,然后每个线程分别对count增加40000000 值,这个值是我随便选的,只要大一点就行,但是别超了2^32。而count1和count2分别来记录两个线程对count分别增加了多少次,其实有NUM控制就好了,不过为了对比,我们加入这两个变量。主进程创建两个线程后我们用pthread_join函数来等待两个线程执行完毕,并打印三个值比较得出结果。

首先在PC机上看下结果,CPU是双核2.6GHZ的,运行环境是ubuntu,顺便用time命令查看下执行时间:

从上图可以看出,两个线程对count进行总共80000000次累加大概需要2ms多一点,测了6次有2次是有问题的,即count != count1 + count2,概率还是比较大的。

然后我把相同的代码重新编译拿到AM335x(TI A8单核600MHZ)运行,结果如下

这个时间程序耗时就明显长了,需要大概4s,本来我以为单核处理器出错概率会小,没想到运行5次结果居然全是错的。具体为什么会这样没去深究,猜想应该和SMP机制及操作系统线程调度有关。这个结果更证明了线程同步的重要性,尤其是在嵌入式系统中。

3.同步问题解决方案

既然问题都明白了,接下来当然是解决方案了,解决这种同步问题最经典的方案就是锁了,相信大部分人平时都用过。以linux线程库提供的接口,代码改为下面形式。

#define NUM 40000000
pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;

pthread_t tid1;
pthread_t tid2;

unsigned int count1 = 0;
unsigned int count2 = 0;
unsigned int count = 0;

void * thr_fn1(void *arg)
{
	while(count1<NUM)
	{
		pthread_mutex_lock(&lock);
		count++;
		pthread_mutex_unlock(&lock);

		count1++;
	}
}

void * thr_fn2(void *arg)
{
	while(count2<NUM)
	{
		pthread_mutex_lock(&lock);
		count++;
		pthread_mutex_unlock(&lock);

		count2++;
	}
}

只列出了部分代码,其它的都一样,其实思想很简单,就是在并发访问同一个变量时候,给这个共享变量加锁,保证写操作的原子性即可。那么为什么count1和count2不用加锁呢,因为两个变量本身就只在两个线程中分别操作,所以没必要加锁。

最后来看下结果,问题本身已经解决了,但是这次重点不在结果上,而在程序执行时间上

这是在PC上结果

这是在ARM上结果

加了这个操作后,PC上同一程序运行时间多了10倍,板子上多了6倍。所以加锁操作在保证了并发访问正确性同时,大大增加了程序运行时间。所以我们在多进程共享资源并发访问程序设计时候,需要综合考虑程序的正确性和效率。

时间: 2024-08-10 02:10:38

同步问题总结的相关文章

Oracle 10g通过创建物化视图实现不同数据库间表级别的数据同步

摘自:http://blog.csdn.net/javaee_sunny/article/details/53439980 目录(?)[-] Oracle 10g 物化视图语法如下 实例演示 主要步骤 在A节点创建原表和物化视图日志 在B节点创建连接A节点的远程链接 在B节点处创建目标表和与目标表名称相同的物化视图 在B节点处刷新物化视图 升级采用存储过程定时任务JOB方式定时刷新物化视图 进一步优化 文章更新记录 参考文章 Oracle 10g 物化视图语法如下: create materia

rsync+inotify-tools实时同步

rsync概述: rsync是类unix系统下的数据镜像备份工具--remote sync.一款快速增量备份工具 Remote Sync,远程同步 支持本地复制,或者与其他SSH.rsync主机同步 Rsync(remote sync)是UNIX及类UNIX平台下一款神奇的数据镜像备份软件,它不像FTP或其他文件传输服务那样需要进行全备份,Rsync可以根据数据的变化进行差异备份,从而减少数据流量,提高工作效率.你可以使用它进行本地数据或远程数据的复制,Rsync可以使用SSH安全隧道进行加密数

使用Second Copy同步ftp服务器的差异文件

公司一直用自主开发的一个同步工具来进行数据库文件异机备份的,但无奈太不稳定,三天两头出现服务挂死的问题,特别是最近这1个月,几天就1次. 问题现象都是服务一直在运行,但没有复制文件到备份机上,而且备份服务重启需要2个小时到2天…… 原因估计是网络问题导致文件共享复制中断,无法结束正在复制文件的这个进程而引起的.具体原因不想深究,那是个无底洞. 所以前天开始,就开始在各大软件网站和论坛中,搜索满足需求的同步软件,要求如下: 软件安装在客户端,通过ftp服务,定期从服务器上下载差异文件 支持设置执行

已经在Git Server服务器上导入了SSH公钥,可用TortoiseGit同步代码时,还是提示输入密码?

GitHub虽好,但毕竟在国内访问不是很稳定,速度也不快,而且推送到上面的源码等资料必须公开,除非你给他交了保护费:所以有条件的话,建议大家搭建自己的Git Server.本地和局域网服务器都好,不信你试试,那速度,怎一个爽字了得! 默认情况下,使用TortoiseGit同步代码,每次都需要输入用户名和密码,但为了方便可以在客户端创建ssh密钥,用于服务器端和客户端的认证(详细过程大家可参考这里),但有时会出现“ 已经在Git Server服务器上导入了SSH公钥,可用TortoiseGit同步

Rsync无差异同步原理和实现

4.Rsync无差异同步原理和实现.md 无差异同步   在生产环境中我们可能会遇到这样的状况,我们的客户端在服务端去拉取数据的时候,首先一点,对于拉取我们的服务端有的那么我们客户端肯定有.但是我们的客户端有的服务端就不一定是有的了.反之如果是推送的话,那么我们客户端有的我们的服务端那肯定是有的,但是我们的服务端有的我们的客户端不一定有.所以对于这种当服务端删除一条数据之后为了保障我们的客户端也能在下一次同步的时候进行删除的这种情况我们就需要采取无差异同步,保障数据的一致性. 实现方法 --de

主从同步出现一下错误:Slave_IO_Running: Connecting

主从同步出现一下错误: Slave_IO_Running: Connecting Slave_SQL_Running: Yes 解决方法: 导致lave_IO_Running 为connecting 的原因主要有以下 3 个方面: 1.网络不通 2.密码不对 3.pos不对 我敢肯定的并且排除的两个原因 1. 网络原因:在从库上拷贝主库上的备份文件的时候并没有出现任何的错误,因此可以排除. 2. POS 不对:这个我也可以排除,因为在主库上 show maste status 时我记录过了 PO

rsync+inotify实时同步

一.Rsync简介: rsync是一个远程数据同步工具,可通过lan/wan快速同步多台主机间的文件.它使用所谓的"rsync演算法"来使本地和远程两个主机之间的文件达到同步,这个算法只传送两个文件的不同部分,而不是每次都整份传送,因此速度相当快.所以通常可以作为备份工具来使用. 运行rsync server的机器也叫backup server,一个rsync server可同时备份多个client的数据:也可以多个rsync server备份一个client的数据.rsync可以搭配

Android实现组件之间同步的回调通信

Android开发中,有时会遇到组件之间相互通信回调的问题.一般都是通过Android提供的ResultReceiver来实现(ResultReceiver的使用方法很简单,这里就不多提了). 但之前在工作中,遇到了一个组件间回调的问题,ResultReceiver无法满足需求.简单描述一下问题:service中打开了一个activity,activity需要将一个变量值回调给service,而且这个回调必须是同步的.也就是说activity在确认service接收到了这个变量值后,才能继续向下

同步异步中的一致性

简述一致性中关于同步与异步环境下的共识理论 (##转载请注明) 共识问题:可称作协作,所有正确的进程对提议的值达成一致.分布式系统中,节点之间通过通信,对请求达成一致的定序. 问题定义:进程Pi处于未决状态(undecideed),提议集合D中的某个值Vi.进程之间相互通信,交换各自的提议.每个进程设置自己的决定变量(decision variable),进入决定状态(decided),此状态下不改变di的数值. 要求满足如下几个性质: 终止性(Termination):正确的进程最终都可以设置

基于OGG的Oracle与Hadoop集群准实时同步介绍

Oracle里存储的结构化数据导出到Hadoop体系做离线计算是一种常见数据处置手段.近期有场景需要做Oracle到Hadoop体系的实时导入,这里以此案例做以介绍.Oracle作为商业化的数据库解决方案,自发性的获取数据库事务日志等比较困难,故选择官方提供的同步工具OGG(Oracle GoldenGate)来解决. 安装与基本配置 环境说明 软件配置 角色 数据存储服务及版本 OGG版本 IP 源服务器 OracleRelease11.2.0.1 Oracle GoldenGate 11.2