无锁编程(三) - 忙等待
时间:2022-04-22
本文章向大家介绍无锁编程(三) - 忙等待,主要内容包括概念、忙等待分类、Peterson算法、xchg解法、TSL解法(Test and Set Lock)、自旋锁、基本概念、基础应用、原理机制和需要注意的事项等,并结合实例形式分析了其使用技巧,希望通过本文能帮助到大家理解应用这部分内容。
概念
忙等待可以认为是一种特殊的忙等待
忙等待分类
Peterson算法
xchg解法
TSL解法
自旋锁
Peterson算法
Peterson算法是一个实现互斥锁的并发程序设计算法,可以控制两个线程访问一个共享的单用户资源而不发生访问冲突。GaryL. Peterson于1981年提出此算法。
#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/time.h>
#include <stdint.h>
int count = 0;
#define N 2
volatile int turn;
volatile int interested[N] = {0};
void enter_region(int process)
{
int other = 1 - process; //另一个进程
interested[process] = true;
turn = process;
while (turn == process && interested[other] == true) NULL; //一直循环,直到other进程退出临界区
}
void leave_region(int process)
{
interested[process] = false; // leave critical region
}
void *test_func(void *arg)
{
int process = *((int *)arg);
printf("thread %d runn", process);
int i=0;
for(i=0;i<2000000;++i)
{
enter_region(process);
//printf("%d enter, count = %dn", pthread_self(),count);
++count;
leave_region(process);
}
return NULL;
}
int main(int argc, const char *argv[])
{
pthread_t id[N];
int process[N];
int i = 0;
uint64_t usetime;
struct timeval start;
struct timeval end;
gettimeofday(&start,NULL);
for(i=0;i<N;++i)
{
process[i] = i;
}
for(i=0;i<N;++i)
{
pthread_create(&id[i],NULL,test_func,&process[i]);
}
for(i=0;i<N;++i)
{
pthread_join(id[i],NULL);
}
gettimeofday(&end,NULL);
usetime = (end.tv_sec-start.tv_sec)*1000000+(end.tv_usec-start.tv_usec);
printf("count = %d, usetime = %lu usecsn", count, usetime);
return 0;
}
结果说明:
[root@rocket lock-free]#./busywait_peterson
thread 0 run
thread 1 run
count = 3999851, usetime = 263132 usecs
可以看出,虽然是互斥算法,但是实测的结果缺不是十分精确,有少量的count丢失,这点让人感到很差异,这里先不去深究,有经验的同学可以帮忙分析一下原因。
xchg解法
#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <sys/types.h>
#include <asm/system.h>
#include <sys/time.h>
#include <stdint.h>
volatile int in_using = 0;
int count = 0;
#define N 2
void enter_region()
{
while (xchg(&in_using, 1)) NULL;
}
void leave_region()
{
in_using = 0; // leave critical region
}
void *test_func(void *arg)
{
int i=0;
for(i=0;i<2000000;++i)
{
enter_region();
++count;
leave_region();
}
return NULL;
}
int main(int argc, const char *argv[])
{
pthread_t id[20];
int i = 0;
uint64_t usetime;
struct timeval start;
struct timeval end;
gettimeofday(&start,NULL);
for(i=0;i<N;++i)
{
pthread_create(&id[i],NULL,test_func,NULL);
}
for(i=0;i<N;++i)
{
pthread_join(id[i],NULL);
}
gettimeofday(&end,NULL);
usetime = (end.tv_sec-start.tv_sec)*1000000+(end.tv_usec-start.tv_usec);
printf("count = %d, usetime = %lu usecsn", count, usetime);
return 0;
}
结果说明:这个结果自然是非常精确,感觉比peterson算法靠谱多了,性能倒是差别不大。
[root@rocket lock-free]# ./busywait_xchg
count = 4000000, usetime = 166548 usecs
TSL解法(Test and Set Lock)
enter_region:
tsl register, lock |复制lock到寄存器,并将lock置为1
cmp register, #0 | lock等于0吗?
jne enter_region |如果不等于0,已上锁,再次循环
ret |返回调用程序,进入临界区
leave_region:
move lock, #0 |置lock为0
ret |返回调用程序
自旋锁
自旋锁请参考我的另一篇文章,这里不再赘述。
https://cloud.tencent.com/developer/article/1020651 版权声明:本文为博主原创文章,未经博主允许不得转载。
- [Go 语言社区]测试模块之---utf8例子
- org.hibernate.type.StringType cannot be cast to org.hibernate.type.VersionType
- JBPM4.4(2)-state结点和decision结点
- [Go 语言社区] Golang架构底层---日志函数
- [Go 语言社区]服务器游戏用户登陆数据读取函数
- 设计Go API的管道使用原则
- Goroutine背后的系统知识
- 从零到 Go:Google感恩节火鸡涂鸦开发纪实
- JBPM4.4(1)-简单工程的搭建
- 未绑定元素“mx:Panel”的前缀“mx”
- [Go 语言社区] Golang架构底层函数图片保存-原创
- [Go 语言社区]Golang架构--服务器与客户端自定义传输规则--原创
- Go语言 -浮点数
- android开发列表界面
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- Android控件AppWidgetProvider使用方法详解
- R语言使用链梯法Chain Ladder和泊松定律模拟和预测未来赔款数据
- Android ViewPager实现左右滑动的实例
- R语言通过伽玛与对数正态分布假设下的广义线性模型对大额索赔进行评估预测
- R语言中回归模型预测的不同类型置信区间应用比较分析
- 第06期:Prometheus 存储
- 新特性解读 | 数组范围遍历功能
- 技术分享 | MySQL 内存管理初探
- 新特性解读 | 窗口函数的适用场景
- Android自定义View 仿QQ侧滑菜单的实现代码
- Android view随触碰滑动效果
- TextView使用SpannableString设置复合文本 SpannableString实现TextView的链接效果
- FragmentTabHost使用方法详解
- Android编程实现仿优酷圆盘旋转菜单效果的方法详解【附demo源码下载】
- Android绘制圆形百分比加载圈效果