skynet中的各种锁
最近读skynet c语言部分的源码,发现有好多锁的使用和gcc提供的一些原子操作。看到这些东西,对于我这个newbee来说实在有些hold不住。但为了了解并进一步掌握,还是决定好好分析一下。不足之处望指正。
自旋锁(spinlock) 和 互斥锁(mutex) 对比
自旋锁:得到锁之前是在一个循环中空转,直到得到锁为止,那么就有三种可能 1:很短时间就得到锁,由于是空转,没有sleep,也就没有由系统到用户态的消耗,2:很长时间才得到锁,虽然没有状态的切换,但是由于忙等时间过长
导致性能下降,3:一直空转,消耗cpu时间。
互斥锁 : 企图获得锁,若是得不到锁则阻塞,放弃cpu,没有忙等的出现,当锁可得时,发生状态切换,由内核切换到用户态,虽然没有忙等但是状态切换的代价仍然很大。
由此可知:对自旋锁和互斥锁的选择是要根据得到锁的耗时来的,若果当得到锁后,需要执行大量的操作,一般选用互斥锁,若得到锁后,进行很少量的操作,一般选择自旋锁,因为执行的操作短,那么忙等的开销总体还是小于内核态
和用户态切换带来的开销的。
最近在使用skynet,这里贴出来cloud实现自旋锁的代码,方便大家查阅:
#ifndef SKYNET_SPINLOCK_H
#define SKYNET_SPINLOCK_H
#define SPIN_INIT(q) spinlock_init(&(q)->lock);
#define SPIN_LOCK(q) spinlock_lock(&(q)->lock);
#define SPIN_UNLOCK(q) spinlock_unlock(&(q)->lock);
#define SPIN_DESTROY(q) spinlock_destroy(&(q)->lock);
#ifndef USE_PTHREAD_LOCK
struct spinlock {
int lock;
};
static inline void
spinlock_init(struct spinlock *lock) {
lock->lock = 0;
}
static inline void
spinlock_lock(struct spinlock *lock) {
while (__sync_lock_test_and_set(&lock->lock,1)) {}
}
static inline int
spinlock_trylock(struct spinlock *lock) {
return __sync_lock_test_and_set(&lock->lock,1) == 0;
}
static inline void
spinlock_unlock(struct spinlock *lock) {
__sync_lock_release(&lock->lock);
}
static inline void
spinlock_destroy(struct spinlock *lock) {
(void) lock;
}
#else
#include <pthread.h>
// we use mutex instead of spinlock for some reason
// you can also replace to pthread_spinlock
struct spinlock {
pthread_mutex_t lock;
};
static inline void
spinlock_init(struct spinlock *lock) {
pthread_mutex_init(&lock->lock, NULL);
}
static inline void
spinlock_lock(struct spinlock *lock) {
pthread_mutex_lock(&lock->lock);
}
static inline int
spinlock_trylock(struct spinlock *lock) {
return pthread_mutex_trylock(&lock->lock) == 0;
}
static inline void
spinlock_unlock(struct spinlock *lock) {
pthread_mutex_unlock(&lock->lock);
}
static inline void
spinlock_destroy(struct spinlock *lock) {
pthread_mutex_destroy(&lock->lock);
}
#endif
#endif
linux自带了pthread_spinlock。
cloud的第一种实现中,用到了gcc自带的原子操作函数实现了spinlock,这里提供一些gcc自带的原子操作的资料:https://gcc.gnu.org/onlinedocs/gcc-4.7.2/gcc/_005f_005fatomic-Builtins.html#_005f_005fatomic-Builtins
互斥锁和条件锁
pthread_mutex_t 和 pthread_cond_t
在阅读skynet.start.c中发现了这两种锁的使用,代码过长,不全部列出,这里只列出简化过得。
源码在这里:https://github.com/cloudwu/skynet/blob/master/skynet-src/skynet_start.c
简化后代码:
//创建线程时调用的代码
static void
create_thread(pthread_t *thread, void *(*start_routine) (void *), void *arg) {
if (pthread_create(thread,NULL, start_routine, arg)) {
fprintf(stderr, "Create thread failed");
exit(1);
}
}
//事件发生时调用
static void
wakeup(struct monitor *m, int busy) {
if (m->sleep >= m->count - busy) {
// signal sleep worker, "spurious wakeup" is harmless
pthread_cond_signal(&m->cond);
}
}
//创建socket线程监听链接,并唤醒阻塞的thread_worker线程
static void *
thread_socket(void *p) {
struct monitor * m = p;
skynet_initthread(THREAD_SOCKET);
for (;;) {
int r = skynet_socket_poll();
if (r==0)
break;
if (r<0) {
CHECK_ABORT
continue;
}
wakeup(m,0);
}
return NULL;
}
static void *
thread_worker(void *p) {
struct worker_parm *wp = p;
int id = wp->id;
int weight = wp->weight;
struct monitor *m = wp->m;
struct skynet_monitor *sm = m->m[id];
skynet_initthread(THREAD_WORKER);
struct message_queue * q = NULL; //上边几句选择性忽略
while (!m->quit) {
q = skynet_context_message_dispatch(sm, q, weight);
if (q == NULL) {
if (pthread_mutex_lock(&m->mutex) == 0) { //创建工作线程时,每个线程运行到这里,获得mutex
++ m->sleep; //当没有事件要处理时,阻塞贤臣数加1
// "spurious wakeup" is harmless,
// because skynet_context_message_dispatch() can be call at any time.
if (!m->quit)
pthread_cond_wait(&m->cond, &m->mutex); //在这里真正的阻塞在cond上,调用这个函数时,线程阻塞在cond上,并暂时放弃mutex的使用权,让其他线程可 以获取到,当其他线程获得到mutex,继续阻塞在cond上,直到socket线程监听到链接,调用pthread_cond_signal来唤醒工作线程,唤醒的工作线程,重新获得mutex.
-- m->sleep;
if (pthread_mutex_unlock(&m->mutex)) {
fprintf(stderr, "unlock mutex error");
exit(1);
}
}
}
}
return NULL;
}
创建thread_worker线程的代码: create_thread(&pid[i+3], thread_worker, &wp[i]);
以上代码大致工作过程:
1.create_thread(&pid[i+3], thread_worker, &wp[i]); 创建工作线程
2.运行到thread_worker函数中pthread_cond_wait(&m->cond, &m->mutex)时,线程阻塞在cond上,待等待的条件出现,方可被唤醒.
3.所有创建的工作线程,都阻塞在了cond上,等待条件出现,而条件出现是在用户连接服务端后,被epoll或kqueue监听到后,唤醒工作线程
int r = skynet_socket_poll();
if (r==0)
break;
if (r<0) {
CHECK_ABORT
continue;
}
wakeup(m,0); //唤醒工作线程
一般来说 mutex和cond是配合使用的,稍后解释原因。
未完,待续,欢迎纠正错误。
skynet中的各种锁的更多相关文章
- Java中的显示锁 ReentrantLock 和 ReentrantReadWriteLock
在Java1.5中引入了两种显示锁,分别是可重入锁ReentrantLock和可重入读写锁ReentrantReadWriteLock.它们分别实现接口Lock和ReadWriteLock.(注意:s ...
- 在 Java 中高效使用锁的技巧--转载
竞争锁是造成多线程应用程序性能瓶颈的主要原因 区分竞争锁和非竞争锁对性能的影响非常重要.如果一个锁自始至终只被一个线程使用,那么 JVM 有能力优化它带来的绝大部分损耗.如果一个锁被多个线程使用过,但 ...
- 分门别类总结Java中的各种锁,让你彻底记住
概念 公平锁/非公平锁 公平锁是指多个线程按照申请锁的顺序来获取锁. 非公平锁是指多个线程获取锁的顺序并不是按照申请锁的顺序,有可能后申请的线程比先申请的线程优先获取锁.有可能,会造成优先级反转或者饥 ...
- SQLSERVER中的元数据锁
SQLSERVER中的元数据锁 网上对于元数据锁的资料真的非常少 元数据锁一般会出现在DDL语句里 下面列出数据库引擎可以锁定的资源 资源 说明 RID 用于锁定堆(heap)中的某一行 KEY 用于 ...
- 编写高质量代码改善C#程序的157个建议——建议89:在并行方法体中谨慎使用锁
建议89:在并行方法体中谨慎使用锁 除了建议88所提到的场合,要谨慎使用并行的情况还包括:某些本身就需要同步运行的场合,或者需要较长时间锁定共享资源的场合. 在对整型数据进行同步操作时,可以使用静态类 ...
- Java 中15种锁的介绍:公平锁,可重入锁,独享锁,互斥锁,乐观锁,分段锁,自旋锁等等
Java 中15种锁的介绍 Java 中15种锁的介绍:公平锁,可重入锁,独享锁,互斥锁,乐观锁,分段锁,自旋锁等等,在读很多并发文章中,会提及各种各样锁如公平锁,乐观锁等等,这篇文章介绍各种锁的分类 ...
- skynet中动态库的处理
skynet中的.so动态库由service-src中的c文件编译完后生成,其中最重要的是snlua.c. 源码地址:https://github.com/cloudwu/skynet/service ...
- Java中的各种锁--分类总结
前言 本文需要具备一定的多线程基础才能更好的理解. 学习java多线程时,最头疼的知识点之一就是java中的锁了,什么互斥锁.排它锁.自旋锁.死锁.活锁等等,细分的话可以罗列出20种左右的锁,光是看着 ...
- mysql中的乐观锁和悲观锁
mysql中的乐观锁和悲观锁的简介以及如何简单运用. 关于mysql中的乐观锁和悲观锁面试的时候被问到的概率还是比较大的. mysql的悲观锁: 其实理解起来非常简单,当数据被外界修改持保守态度,包括 ...
随机推荐
- SQL Server 还原错误“restore database正在异常终止 错误 3154”
今天在还原数据库时,先建立相同名字的数据库,然后在该数据库上右键还原数据库.遇到了这样的一个错误: “备份集中的数据库备份与现有的 'RM_DB' 数据库不同. RESTORE DATABASE 正在 ...
- spoj 10628
http://www.spoj.com/problems/COT/ 树上第k小元素 LCA + 可持久化线段树 每个新的版本都是由其父亲版本转化而来. #include <cstdio> ...
- EasyNVR智能云终端接入AI视频智能分析功能,用户可自定义接入自己的分析算法
视频分析的需求 人工智能的发展和在行业中的作用就不用多说了,已经到了势在必行的一个程度了,尤其是对于流媒体音视频行业来说,这基本上是人工智能重中之重的领域,视频人工智能,也就是视频视觉分析的应用方式大 ...
- Linux下Solr的安装和配置
一.安装 1.需要的安装包:apache-tomcat-7.0.47.tar.gz.solr-4.10.3.tgz.tgz(jdk自行安装) 2.解压tomcat并创建solr文件夹 [root@lo ...
- php无法连接mongodb 3.0问题解决
1 几个常用的role root mongodb最高权限 userAdmin 自己建立的数据库账号管理权限 read 只读权限 readWrite 可读可写 2 遭遇的梗 为数据库建立了账号,php死 ...
- 解决IE,z-index失效
在影响显示顺序的模块加上: style="position:relative;z-index:-1;" 解决IE,z-index失效
- velocity 遍历EventHandler Iterator
EventHandlerUtil 类的 iterateOverEventHandlers方法 for (Iterator i = handlerIterator; i.hasNext();){ Eve ...
- MySQL第二天
回顾 数据库基础知识: 关系型数据库(磁盘)和非关系型数据库(内存) 关系型数据库: 建立在关系模型上的数据库 数据结构: 二维表(比较浪费空间) 操作数据的指令集合: SQL(DDL,DML ...
- Linux中的系统默认日志
/var/log/cron 记录了系统定时任务相关的日志 /var/log/cups 记录了打印信息的日志 /var/log/dmesg 记录了系统在开机时内核自检的信息,可以通过dmesg命令直接查 ...
- 免费 SSL 安全证书
为了保证网上传输信息的安全而在自己的 Linode VPS 上部署 SSL 加密服务.商业 CA 较贵,所以使用了自己签发的 CA.网友神爱的留言提到了 StartSSL 的免费 CA,稍做了一些调查 ...