杨乾成 2017310500302

一、题目要求

  基于CAS(Compare and Swap)实现一个无锁结构,可考虑queue,stack,hashmap,freelist等。

  • 能够支持多个线程同时访问该结构
  • 不能有任何锁操作,且操作是线程安全的
  • 对上述的内存单元进行管理,至少malloc与free一次。

二、数据结构

  看到题目有一说一,不知道怎么下手,那就google一下先。稍微了解了一下CAS,原准备使用STL模板的队列,后来发现实现题目要求似乎得再Queue的插入和删除函数里面具体实现,于是决定自己实现吧(省事失败)。

  先简要介绍一下CAS算法,我先po一下wiki百科上的代码介绍:

bool compare_and_swap (int *accum, int *dest, int newval)
{
if ( *accum == *dest ) {
*dest = newval;
return true;
}
return false;
}

  这段代码的思想大概就是看看*dest里面的值和*accum里面的值是不是一样,如果一样,则将新数据写入。个人觉得用数据库的“脏数据”概念来形容很合适:即不允许往存有脏数据的缓冲区写入数值。在数据库中,常用的方法是两段锁,但是这里不让加锁。突然想起来大二上学期修JAVA里面讲到的同步信号量,即每当对数据结构进行操作时,就对=改变信号量的值,比如只有信号量值为0的时候才允许更改变量,修改时使用者将信号量置一,但是仔细一想,这似乎也是锁的思想。

  遇事不决,再看看,害,现成的CAS都在这里,我直接用不就完事了。回归正题,CAS思想总结如下:线程操作钱保存当前版本号,也就是记录当前数据的状态的标志;当完成操作时,比较自己持有的版本号与当前版本号是否相符,相符则允许写入,否则获取的新的版本号再来一轮。就好像是大家都听说身边有个好看的姑娘处于“单身版本”,各自回去准备准备,第一个到的获得了优先权,后来的只能记住另一个“单身”的姑娘,再回家准备展开竞争,直到自己完成任务。

三、代码实现

#include<stdio.h>
#include<pthread.h>
#include <unistd.h>
#include <assert.h>
#include<stdlib.h>
#define THREAD_NUMBER 4
//队节点
typedef struct QNode
{
int data;
struct QNode *next;
}QNode, *QueuePtr;
//链队结构
typedef struct LinkQueue
{
QueuePtr front;
QueuePtr rear;
}LinkQueue; int is_Empty(LinkQueue *q);
void *pushThread(void *arg);
void *popThread(void *arg);
void init_Queue(LinkQueue *q);void cas_push(LinkQueue *q, int e);
void cas_push(LinkQueue *q, int e);
int cas_pop(LinkQueue *q, int *e);
void show(LinkQueue *q); int main()
{
LinkQueue que;
init_Queue(&que);
int i; //创建四个进程
pthread_t threadArr[THREAD_NUMBER]; //线程ID数组
for (i = ; i < THREAD_NUMBER; ++i)
{
pthread_create(&threadArr[i], NULL, pushThread, (void *)&que); //设置线程入口
}
//等待线程执行结束
for (i = ; i < THREAD_NUMBER; ++i)
{
pthread_join(threadArr[i], NULL);
}
show(&que); //创建四个进线程然后执行出队操作
for (i = ; i < THREAD_NUMBER; ++i)
{
pthread_create(&threadArr[i], NULL, popThread, (void *)&que);
}
for (i = ; i < THREAD_NUMBER; ++i)
{
pthread_join(threadArr[i], NULL);
}
exit(EXIT_SUCCESS);
} void *pushThread(void *arg)
{
printf("进程%ld开始创建队列:\n",pthread_self());
LinkQueue *quePtr =(LinkQueue *)arg;
int i;
for(i= ;i<;++i)
{
cas_push(quePtr, i);
}
printf("\n");
printf("完成队列创建操作!\n");
pthread_exit(NULL); //结束当前进程
} void *popThread(void *arg)
{
printf("%ld开始出队操作:\n",pthread_self());
LinkQueue *quePtr=(LinkQueue *)arg;
int temp;
int res;
while()
{
res = cas_pop(quePtr,&temp);
if(!res)
{
break;
}
printf("PID:%ld\tpop%d\n",pthread_self(),temp);
} } //初始化队列
void init_Queue(LinkQueue *q)
{
q->front=q->rear=(QNode *)malloc(sizeof(QNode));
q->front->next=NULL;
} //判断队列是否为空
int is_Empty(LinkQueue *q)
{
if(q->front->next ==NULL)
{
return ;
}
return ;
} void cas_push(LinkQueue *q, int e)
{
QueuePtr newNode = (QueuePtr)malloc(sizeof(QNode));
newNode->data = e;
newNode->next = NULL;
QueuePtr tmp;
//不断尝试将新节点进队
do
{
// sleep(1);
tmp = q->rear;
}while (!__sync_bool_compare_and_swap((long *)(&(tmp->next)), NULL, (long)newNode)); //判断此时队尾是否被改变
//就是在这里控制对队列的原子化操作
sleep();
q->rear = newNode;
} int cas_pop(LinkQueue *q, int *e)
{
QueuePtr tmp;
do
{
if (is_Empty(q))
{
return(); //队列已空,结束
}
tmp = q->front->next;
} while (!__sync_bool_compare_and_swap((long *)(&(q->front->next)), (long)tmp, (long)tmp->next));
sleep(); //通过休眠一秒,模拟线程操作时间,防止过快操作导致结果不明显
*e = tmp->data;
free(tmp);
return ;
} void show(LinkQueue *q)
{
printf("Current queue:\n");
QueuePtr tmp = q->front->next;
while (tmp)
{
printf("%d ", tmp->data);
tmp = tmp->next;
}
printf("\n");
}

上面就是这次的代码实现。

四、运行结果

使用指令:

gcc CAS_Structure.c -o result.out -lpthread

将源码进行编译,此处要注意后面一定要加上-lpthread,不然会提示“pthread.h”头文件报错问题。

我创建了四个进程,竞争往队尾插入数据。

  然后竞争删除完队列的数据,直到队列为空。

  从截图可以看出来,四个进程竞争着抢占处理器,直到完成创建和删除节点的任务。这里面要注意的是,因为实际处理就是插入节点,刚开始做的时候我没有插入sleep语句,所以最后的结果看起来像是顺序完成,所以要模拟实验,一定要注意加上sleep语句模拟进程处理的时间,防止第二个进程还没创建完,第一个进程早已完成了所有事务。

基于CAS实现无锁结构的更多相关文章

  1. paip.提升性能----java 无锁结构(CAS, Atomic, Threadlocal, volatile, 函数式编码, 不变对象)

    paip.提升性能----java 无锁结构(CAS, Atomic, Threadlocal, volatile, 函数式编码, 不变对象) 1     锁的缺点 2     CAS(Compare ...

  2. 使用CAS实现无锁的SkipList

    无锁 并发环境下最常用的同步手段是互斥锁和读写锁,例如pthread_mutex和pthread_readwrite_lock,常用的范式为: void ConcurrencyOperation() ...

  3. 并发-AtomicInteger源码分析—基于CAS的乐观锁实现

    AtomicInteger源码分析—基于CAS的乐观锁实现 参考: http://www.importnew.com/22078.html https://www.cnblogs.com/mantu/ ...

  4. AtomicInteger源码分析——基于CAS的乐观锁实现

    AtomicInteger源码分析——基于CAS的乐观锁实现 1. 悲观锁与乐观锁 我们都知道,cpu是时分复用的,也就是把cpu的时间片,分配给不同的thread/process轮流执行,时间片与时 ...

  5. 聊聊高并发(三十二)实现一个基于链表的无锁Set集合

    Set表示一种没有反复元素的集合类,在JDK里面有HashSet的实现,底层是基于HashMap来实现的.这里实现一个简化版本号的Set,有下面约束: 1. 基于链表实现.链表节点依照对象的hashC ...

  6. AtomicInteger源码分析——基于CAS的乐观锁实

    1. 悲观锁与乐观锁 我们都知道,cpu是时分复用的,也就是把cpu的时间片,分配给不同的thread/process轮流执行,时间片与时间片之间,需要进行cpu切换,也就是会发生进程的切换.切换涉及 ...

  7. CAS实现无锁模式

    用多线程实现一个数字的自增长到1000000,分别用无锁模式和锁模式来实现代码. 1.使用ReentrantLock. package test; import java.util.concurren ...

  8. 使用CAS实现无锁列队-链表

    #include <stdlib.h> #include <stdio.h> #include <pthread.h> #include <iostream& ...

  9. 图解kubernetes scheduler基于map/reduce无锁设计的优选计算

    优选阶段通过分离计算对象来实现多个node和多种算法的并行计算,并且通过基于二级索引来设计最终的存储结果,从而达到整个计算过程中的无锁设计,同时为了保证分配的随机性,针对同等优先级的采用了随机的方式来 ...

随机推荐

  1. yum安装软件报错Error: Nothing to do

    今天在一台新服务器上装一些常用软件,一开始安装ncdu(一个很好用的磁盘分析工具,用来查找大文件),报错如下: 在网上找了各种办法,什么更新yum啊,清理yum缓存啊的,统统没用 最后的找到的问题是, ...

  2. fiddler 抓取winform wcf包

    修改客户端配置 <system.net> <defaultProxy> <proxy bypassonlocal="false" usesystemd ...

  3. 运维相关指标数据采集并ES入仓 - 运维笔记

    为了进行数字化IT治理,需要对一些应用进程相关指标进行采集并入库.收集到的应用指标数据最好要进行ES入仓,入到Kafka里面,并通过Kibana可视化展示. 需要进行采集的应用进程相关指标如下: ES ...

  4. python每日学习2018/1/14(python之禅)

    The Zen of Python, by Tim Peters   Beautiful is better than ugly. Explicit is better than implicit. ...

  5. SQL Server 判断各种对象是否存在和sysobjects的关系

    一.判断表是否存在 object_id():获取表的ID,其中N表示Unicode类型.可以支持不同语种的对象名 ) drop table [dbo].[表名] 二.判断要创建的存储过程名是否存在 ) ...

  6. Java使用路径通配符加载Resource与profiles配置使用

    序言 Spring提供了一种强大的Ant模式通配符匹配,能从一个路径匹配一批资源. Ant路径通配符 Ant路径通配符支持“?”.“*”.“**”,注意通配符匹配不包括目录分隔符“/”: “?”:匹配 ...

  7. 【UOJ#390】【UNR#3】百鸽笼(动态规划,容斥)

    [UOJ#390][UNR#3]百鸽笼(动态规划,容斥) 题面 UOJ 题解 发现这就是题解里说的:"火山喷发概率问题"(大雾 考虑如果是暴力的话,你需要记录下当前每一个位置的鸽笼 ...

  8. SQL Server中使用SQL语句关闭数据库连接和删除数据库文件

    有时候我们想用DROP DATABASE语句删除数据库和数据库文件,会删不掉,因为有其他人正在使用要删除的数据库,这里有一个方法可以强制断开其它数据库连接,再删除数据库. 假如我们要删除的数据库是[T ...

  9. Eureka服务下线源码解析

    我们知道,在Eureka中,可以使用如下方法使Eureka主动下线,那么本篇文章就来分析一下子这个下线的流程 public synchronized void shutdown() { if (isS ...

  10. English--美式发音

    English|美式发音 本文,总结了自己在学习美音的一些感悟,希望大家学习愉快!enjoy~ 前言 目前所有的文章思想格式都是:知识+情感. 知识:对于所有的知识点的描述.力求不含任何的自我感情色彩 ...