- 浏览: 334143 次
- 性别:
- 来自: 福建福州
文章分类
最新评论
-
jw72jw:
最后这个是打表求值
LUA源码分析三:table分析(1) -
dyllove98:
"一些非常重要的问题,涉及面少。那这个时候,我更崇尚 ...
乱写:团队里的独裁和民主一点看法 -
jvmlover:
被踩10次了,什么思想感情啊。
LUA源码分析三:table分析(1) -
chenchenfly99:
chenchenfly99 写道
MMO游戏终极内测开服一周,问题记录 -
chenchenfly99:
...
MMO游戏终极内测开服一周,问题记录
以下代码和资料均学习自:《进程间通信》第8章读写锁
其中附件中的代码为自己重新封装后的代码和一个测试代码
编译环境如下
Thread model: posix
gcc version 3.2.2 20030222 (Red Hat Linux 3.2.2-5)
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
正文:
读优先的读写锁策略
假设对于一个共享的数据区来说,读/写都需要,而写的并发又可能比较频繁一点,如果按照普通的mutex思路可能是以下结构:
对于write来说,对于共享的形式是独占(shared-exclusive);但对于读锁来说对于共享的形式是共享锁(shared lock),即不需要mutex的互斥保护。那么以上的写法中,对于读的情况效率大大的打折。比如一个银行帐户,多个线程可以同时读出某个帐户的收支结余,但是一旦有一个线程需要更新某个给定收支结余,该线程就必须等待所有读出者完成该收支结余的读出,然后只允许该更新线程修改这个收支结余。那么在实现这个策略时,必须遵循以下三个条件
1、 保证并发的读取,不对读取做什么界限的限制,仅仅以一个计数器来表示当前的读取量
2、 有写入线程时,保证写入线程的独立执行
3、 在有写入线程发生,以这个写入线程时间点为界限,之前的等待读线程要执行完,之后的读线程处于等待状态
那么可以设置这么一个结构体`
再设置两个获取读/写锁函数:
int pthread_RWlock_rdlock (pthread_RWlock_t *pRw);
int pthread_RWlock_wrlock (pthread_RWlock_t *pRw);
流程的判断变为如下的工作流程,这里我先把图给出,问题统一放在后面分析
获取读/写锁两个函数大体流程如下:
pthread_RWlock_wrlock函数流程如下:
pthread_RWlock_rdlock函数流程如下:
问题一:先说明一个技巧
这里用了phtread_cond_wait(), 防止一个不断的轮询过程,代码是这样的
注意,这里用了个while.在多线程的情况下,有多个cond_wait进行等待,当rw_mutex的锁被释放时,可能被其他线程抢到(即不保证被本身的线程取到),并对rw_refcount进行修改。如果不对rw_refcount进行判断而直接执行,就可能造成数据的不准确。
问题二:获取读锁时对rw_refcount!= 0的判断
这要和读锁的rw_refcount <0和rw_nwaitwriters>0两个条件结合起来进行解释。首先假设当前有多个线程进行工作,这时有个写线程进来,发生阻塞,waitwriters自加。这个时候比这个写线程后来的读线程检测到rw_nwaitwriters>0而发生阻塞,在这之前的读线程仍在工作,这里我们还需要知道pthread_RWlock_unlock的部分工作代码
看吧,把之前对读锁的获取时rw_refcount++和这里的rw_refcount—结合起来,可得知,当读线程都工作完毕时,rw_refcount必然会等于0,这也满足了我们开头提出的三个条件中的最后一条。那么剩下的pthread_RWlock_unlock工作代码如下:
首先要先判断当前是否有写的线程等待,这里并不是把优先权给写的线程,而是在有大量的并发读线程下,必须给写的线程点机会,否则它永远都执行不了。这里你可能对pRw->rw_refcount == 0有点疑问。作者的原意可能是这样的:一个写线程在工作中,另一个写线程刚好释放完,准备通知另一个正在写的线程,但这个时候>rw_refcount仍等于-1,通知是无意义的。其实我也是,我认为不会触发这个条件。因为写的线程是确保只有一个执行,而在释放锁的时候已经确保pRw->rw_refcount = 0,当然这会在我代码验证后给出。
好了,现在我们以文字的形式整理下所有的思路。
假设有3个读线程进行并发,那么它们一开始没有检测到rw_refcount <0和rw_nwaitwriters>0,于是他们很欢快的取的读的权限进行并发的读取,假设这个读取的时间很长。。。。这个时候一条写线程进入,它判断rw_refcount不等于0,于是进行阻塞。
在这个时间里,又有3个读线程发生,但是它们检测到了nwaitwriters大于0,也进行阻塞。。终于,最早的3个读线程工作完了,释放完后,通知正在等待的写线程。写线程工作完后,这时候没有其他的写线程进入,所以pthread_cond_broadcast发生了作用,它通知了最后进入的3个读线程。。。
一个大问题,线程被取消掉的死锁
假设有两个线程,读和写,首先保证thread1的先执行
这段代码的工作模式如下:thread1先获取一个读锁,睡眠3秒,thread2阻塞在获取写锁上,thread1取消掉thread2,最后thread1调用unlock函数。这时候阻塞在了unlock里。
为什么?这得了解一个线程的知识,pthread_cond_wait函数发生时,会自动释放掉一个mutex,以运行其他线程进入等待。(如果没有释放这个mutex,那么就没有什么并发等待的意义了,大家都阻塞在最外面的lock),当thread1进行取消时,这个mutex没有释放(或者看成pthread_cond_wait之前的释放mutex没什么效果)。那么在unlock里,也需要对这个mutex进行访问,而造成了死锁。这也是许多多线程里死锁的隐患。具体的解决方案见代码,其实很简单,就是压入一个释放函数,如果线程发生意外直接返回则调用该函数。
其中附件中的代码为自己重新封装后的代码和一个测试代码
编译环境如下
Thread model: posix
gcc version 3.2.2 20030222 (Red Hat Linux 3.2.2-5)
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
正文:
读优先的读写锁策略
假设对于一个共享的数据区来说,读/写都需要,而写的并发又可能比较频繁一点,如果按照普通的mutex思路可能是以下结构:
lock() Write()/Read() Unlock()
对于write来说,对于共享的形式是独占(shared-exclusive);但对于读锁来说对于共享的形式是共享锁(shared lock),即不需要mutex的互斥保护。那么以上的写法中,对于读的情况效率大大的打折。比如一个银行帐户,多个线程可以同时读出某个帐户的收支结余,但是一旦有一个线程需要更新某个给定收支结余,该线程就必须等待所有读出者完成该收支结余的读出,然后只允许该更新线程修改这个收支结余。那么在实现这个策略时,必须遵循以下三个条件
1、 保证并发的读取,不对读取做什么界限的限制,仅仅以一个计数器来表示当前的读取量
2、 有写入线程时,保证写入线程的独立执行
3、 在有写入线程发生,以这个写入线程时间点为界限,之前的等待读线程要执行完,之后的读线程处于等待状态
那么可以设置这么一个结构体`
typedef struct { pthread_mutex_t rw_mutex; /* basic lock on this struct */ pthread_cond_t rw_condreaders; /* for reader threads waiting */ pthread_cond_t rw_condwriters; /* for writer threads waiting */ int rw_magic; /* for error checking */ int rw_nwaitreaders; /* the number waiting */ int rw_nwaitwriters; /* the number waiting */ int rw_refcount; } pthread_RWlock_t; /* *rw_nwaitreaders:当前有读的线程工作时,写的线程阻塞,并且该变量自增 *rw_refcount:表示锁的工作状态,如果是写用-1表示,如果当前无工作状态用0表示,读的 *状态为大于1,其数量代表了当前的读的并发数 */
再设置两个获取读/写锁函数:
int pthread_RWlock_rdlock (pthread_RWlock_t *pRw);
int pthread_RWlock_wrlock (pthread_RWlock_t *pRw);
流程的判断变为如下的工作流程,这里我先把图给出,问题统一放在后面分析
获取读/写锁两个函数大体流程如下:
pthread_RWlock_wrlock函数流程如下:
pthread_RWlock_rdlock函数流程如下:
问题一:先说明一个技巧
这里用了phtread_cond_wait(), 防止一个不断的轮询过程,代码是这样的
while( pRw->rw_refcount != 0 ) { pRw->rw_nwaitwriters++; iResult = pthread_cond_wait(&pRw->rw_condwriters, &pRw->rw_mutex); pRw->rw_nwaitwriters--; if( iResult != 0 ) { break; } }
注意,这里用了个while.在多线程的情况下,有多个cond_wait进行等待,当rw_mutex的锁被释放时,可能被其他线程抢到(即不保证被本身的线程取到),并对rw_refcount进行修改。如果不对rw_refcount进行判断而直接执行,就可能造成数据的不准确。
问题二:获取读锁时对rw_refcount!= 0的判断
这要和读锁的rw_refcount <0和rw_nwaitwriters>0两个条件结合起来进行解释。首先假设当前有多个线程进行工作,这时有个写线程进来,发生阻塞,waitwriters自加。这个时候比这个写线程后来的读线程检测到rw_nwaitwriters>0而发生阻塞,在这之前的读线程仍在工作,这里我们还需要知道pthread_RWlock_unlock的部分工作代码
/* 对读锁的释放为:如果是读锁,refcount减1,信号通知 对写锁的释放为:如果是写锁,refconut直接置0,信号通知 */ if( pRw->rw_refcount > 0 ) { pRw->rw_refcount--; } else if( pRw->rw_refcount == -1 ) { pRw->rw_refcount = 0; }
看吧,把之前对读锁的获取时rw_refcount++和这里的rw_refcount—结合起来,可得知,当读线程都工作完毕时,rw_refcount必然会等于0,这也满足了我们开头提出的三个条件中的最后一条。那么剩下的pthread_RWlock_unlock工作代码如下:
if( pRw->rw_nwaitwriters > 0 ) { /* 防止发送空的signal */ if( pRw->rw_refcount == 0 ) { iResult = pthread_cond_signal(&pRw->rw_condwriters); } } else if( pRw->rw_nwaitreaders > 0 ) { iResult = pthread_cond_broadcast(&pRw->rw_condreaders); }
首先要先判断当前是否有写的线程等待,这里并不是把优先权给写的线程,而是在有大量的并发读线程下,必须给写的线程点机会,否则它永远都执行不了。这里你可能对pRw->rw_refcount == 0有点疑问。作者的原意可能是这样的:一个写线程在工作中,另一个写线程刚好释放完,准备通知另一个正在写的线程,但这个时候>rw_refcount仍等于-1,通知是无意义的。其实我也是,我认为不会触发这个条件。因为写的线程是确保只有一个执行,而在释放锁的时候已经确保pRw->rw_refcount = 0,当然这会在我代码验证后给出。
好了,现在我们以文字的形式整理下所有的思路。
假设有3个读线程进行并发,那么它们一开始没有检测到rw_refcount <0和rw_nwaitwriters>0,于是他们很欢快的取的读的权限进行并发的读取,假设这个读取的时间很长。。。。这个时候一条写线程进入,它判断rw_refcount不等于0,于是进行阻塞。
在这个时间里,又有3个读线程发生,但是它们检测到了nwaitwriters大于0,也进行阻塞。。终于,最早的3个读线程工作完了,释放完后,通知正在等待的写线程。写线程工作完后,这时候没有其他的写线程进入,所以pthread_cond_broadcast发生了作用,它通知了最后进入的3个读线程。。。
一个大问题,线程被取消掉的死锁
假设有两个线程,读和写,首先保证thread1的先执行
void *thread1(void *) { int iResult; CRWLock.pthread_RWlock_rdlock(&rwlock); cout<<"thread 1 got a read lock"<<endl; sleep(3); /*让thread2阻塞在pthread_RWlock_wrlock()*/ pthread_cancel(tid2); sleep(3); iResult = CRWLock.pthread_RWlock_unlock(&rwlock); return 0; } void *thread2(void *) { cout<<"thread 2 trying to obtain a write lock"<<endl; CRWLock.pthread_RWlock_wrlock(&rwlock); cout<<"thread2 got a write lock"<<endl; sleep(1); CRWLock.pthread_RWlock_unlock(&rwlock); return 0; }
这段代码的工作模式如下:thread1先获取一个读锁,睡眠3秒,thread2阻塞在获取写锁上,thread1取消掉thread2,最后thread1调用unlock函数。这时候阻塞在了unlock里。
为什么?这得了解一个线程的知识,pthread_cond_wait函数发生时,会自动释放掉一个mutex,以运行其他线程进入等待。(如果没有释放这个mutex,那么就没有什么并发等待的意义了,大家都阻塞在最外面的lock),当thread1进行取消时,这个mutex没有释放(或者看成pthread_cond_wait之前的释放mutex没什么效果)。那么在unlock里,也需要对这个mutex进行访问,而造成了死锁。这也是许多多线程里死锁的隐患。具体的解决方案见代码,其实很简单,就是压入一个释放函数,如果线程发生意外直接返回则调用该函数。
- myRWlock.rar (3.3 KB)
- 下载次数: 13
发表评论
-
linux内存分配slub的几个疑问
2011-01-13 08:21 1643对于SLUB不熟的同学可以 ... -
开源一个windows下的内存分配器slab,
2010-10-28 20:23 1379模仿linux内核下的slab而写。一些地址页面做了些新的工作 ... -
网络编程中缓冲带来的异步以及需要设置(windows)
2010-08-08 03:50 0此次的疑问主要来自IOCP下的程序编写。 WINDOWS下的 ... -
小议内存池、资源池
2010-08-02 21:08 2155比较简单的一篇文章。本来是有些地方没想明白,想分析一下。结果写 ... -
(00XX系列)抽抽Windows宽字符的棉絮(附日志文件源码)
2009-11-21 17:18 1239爷最近和可 ... -
(00XX系列)摸摸Windows的SEH
2009-11-16 23:13 1624节一:终止处理(Termination Handlers) 节 ... -
配置主机无线网络+虚拟机linux的上网
2009-09-05 11:04 4835环境如下: windows2003主机,虚拟机VM精简版,装有 ... -
碎片:linux vs windows, 内存/硬盘
2009-08-23 13:12 1642找了一堆资料,稍微整 ... -
详解sigaction
2008-12-18 22:24 10534本杂文主要是讲解了下信号和进程的关系。前面主要是一些man式的 ... -
基础:systemV 信号 create send recv rmid
2008-07-26 17:30 1201/* @gcc version 3.2.2 200302 ... -
让您轻松理解execl函数系列 ^_^
2008-06-19 20:03 6844execl函数功能如下:启动一个可执行文件,并且对他进行传送参 ... -
[收集 转载]关于Linux下编写和编译程序的几个问题
2008-04-23 09:09 1859FROM http://www.fmm7.com/jsjc/w ... -
资料存放 linux命令收集和问题记录
2008-03-30 23:03 1367rm -rf name //递归删除 -
Linux基本目录用途
2008-03-24 10:19 1929/bin 该目录中存放Linux的常用命令,在有的版本中是一 ... -
在CentOS 4.4下安装gcc--RPM
2008-03-23 22:28 3362yum install gcc(需要的组件,即你也可以下载以下 ... -
资料存放 yum
2008-03-23 20:47 1070使用 yum 升级和 yum 使用 ... -
资料存放 tar
2008-03-23 20:46 1072tar命令 tar可以为文件和目录创建档案。利用tar,用户可 ... -
初学LINUX:架设一个 VSFTPD服务器系列
2008-03-09 01:33 1553实习的公司需要用到 LINUX,而自己也想深入这方面 公司用的 ... -
虚拟机中设置linux连接网络
2008-03-01 17:21 3791虚拟机:vmware Linux版本:CentOS(版本不会造 ... -
CentOS资料分类
2008-02-08 16:30 1734http://centos.ustc.edu.cn/CentO ...
相关推荐
《UNIX网络编程.第2卷.进程间通信》 (第二版 中文 扫描) 3/4
Linux系统中的进程间通信是典型的虚拟内存系统中的进程间通信:Linux 的进程间通信是不同进程间交换数据的机制。由于Linux系统支持虚拟内 存, 对于每一个进程,系统会为它分配一个单独的进程空间,不同的进程有不同...
《UNIX网络编程.第2卷.进程间通信》 (第二版 中文 扫描) 2/4
《UNIX网络编程.第2卷.进程间通信》 (第二版 中文 扫描) 4/4
《UNIX网络编程.第2卷.进程间通信》 (第二版 中文 扫描)
七种进程间通信方式: 一.无名管道( pipe ) 二.有名管道( fifo ) 三.共享内存 ( shared memory ) 四.信号 ( sinal ) 五.消息队列 ( message queue ) 六.信号量 ( semophore ) 七.套接字 ( socket ) 进程间通信...
共享内存,顾名思义就是允许两个不相关的进程访问同一个逻辑内存,共享内存是两个正在运行的进程之间共享和传递数据的一种非常有效的方式。...1、进程间通信 2、共享内存 3、托管方式与非托管共享内存
Messenger进程间通信,包括Service服务端代码和客户端代码;进程内部通信/进程间通信并且应答;
操作系统实验之进程间通信(管道方式)。 内涵实验源码及报告文档。 更详尽内容请参考小魏博客:http://blog.csdn.net/xiaowei_cqu/article/details/7041212
卷2:进程间通信(第2版)》是一部UNIX网络编程的经典之作!进程间通信(IPC)几乎是所有Unix程序性能的关键,理解IPC也是理解如何开发不同主机间网络应用程序的必要条件。《UNIX网络编程.卷2:进程间通信(第2版)》从对...
以上七种进程间通信的完整代码 皆可以在我的资源列表中获取下载: 资源列表:http: download csdn net user qiulanzhu">进程间通信之信号 sinal ) 唯一的异步通信方式 七种进程间通信方式: 一 无名管道( pipe ...
UNIX网络编程 卷2:进程间通信书中源码
Linux进程间通信
基于c++内存池,共享内存和信号量实现高速的进程间通信队列,单进程读单进程写无需加锁,多进程读多进程写用信号量集实现读写锁保证读写安全
事件同步类CEvent通常用于在应用程序访问资源之前应用程序必须等待的情况(比如,在数据写进一个文件之前数据必须从通信端口得到);互斥同步类CMutex和临界区同步类CCriticalSection都是用于保证一个资源一次只能有一...
Linux进程间通信.pdf Linux进程间通信.pdf
进程间通信,让你清晰了解进程间的通信原理
很详细的介绍linux下进程间通信的书籍.很值得一看的书.
进程间通信之套接字( socket ) 网络间通信 七种进程间通信方式: 一....二....以上七种进程间通信的完整代码,皆可以在我的资源列表中获取下载: 资源列表:http://download.csdn.net/user/qiulanzhu
Linux环境进程间通信(一) 3 Linux环境进程间通信(二): 信号(上) 19 Linux环境进程间通信(二): 信号(下) 30 Linux环境进程间通信(三) 40 Linux环境进程间通信(四) 52 Linux环境进程间通信(五): 共享...