c语言 无锁编程,无锁编程与有锁编程的效率总结、无锁队列的实现(c语言)「建议收藏」

c语言 无锁编程,无锁编程与有锁编程的效率总结、无锁队列的实现(c语言)「建议收藏」1.无锁编程与有锁编程的效率无锁编程,即通过CAS原子操作去控制线程的同步。如果你还不知道什么使CAS原子操作,建议先去查看相关资料,这一方面的资料网络上有很多。CAS实现的是硬件级的互斥,在线程低并发的情况下,其性能比普通互斥锁高效,但是当线程高并发的时候,硬件级互斥引入的代价与应用层的锁竞争产生的代价同样都是很大的。这时普通锁编程其实是优于无锁编程的。硬件级原子操作使应用层的操作变慢,而且无法…

大家好,又见面了,我是你们的朋友全栈君。

1.无锁编程与有锁编程的效率

无锁编程,即通过CAS原子操作去控制线程的同步。如果你还不知道什么使CAS原子操作,建议先去查看相关资料,这一方面的资料网络上有很多。

CAS实现的是硬件级的互斥,在线程低并发的情况下,其性能比普通互斥锁高效,但是当线程高并发的时候,硬件级互斥引入的代价与应用层的锁竞争产生的代价同样都是很大的。这时普通锁编程其实是优于无锁编程的。

硬件级原子操作使应用层的操作变慢,而且无法再进行优化。如果对有锁多线程程序有良好的设计,那么可以使程序的性能在不下降的同时,实现高并发。

2.无锁编程的好处

无锁编程不需要程序员再去考虑死锁、优先反转等棘手的问题,因此在对应用程序不太复杂,而对性能要求稍高的程序中,可以采取有锁编程。如果程序较为复杂,性能要求不高的程序中可以使用无锁编程。

3.无锁队列的实现

对于线程无锁同步方式方式的应用,我实现了一个无锁的队列。首先看一下程序的运行结果:

5389fffa2b83f4d5b2223d91bb76b263.png

程序的运行结果符合队列先进先出的特点。

关于一些细节的问题在代码中都有详细的注释,请参见代码:

#include #include#include#include#include//用链表实现队列

//节点结构

typedef struct Node

{

struct Node *next;

int data;

}node;

//队列的定义

typedef struct Queue

{

node* front;

node* rear;

}queue;

//定义一个全局的队列

queue que;

//队列的初始化操作

void QueInit(queue *que)

{

//申请一个新的节点

node *temp = (node*)malloc(sizeof(node));

assert(temp!=NULL);

temp->next=NULL;

que->front=que->rear=temp;

}

//队空判断

int QueEmpty()

{

return __sync_bool_compare_and_swap(&(que.rear),que.front,que.front);

}

//入队操作

void QuePush(int *d)

{

//申请新节点

node *temp = (node*)malloc(sizeof(node));

assert(temp!=NULL);

temp->data=*d;

//将新申请的节点利用原子操作插入到队列当中

node* p;

do

{

p = que.rear;

}

while(!__sync_bool_compare_and_swap(&(p->next),NULL,temp));

//重置尾指针

__sync_bool_compare_and_swap(&(que.rear),p,temp);

}

//出队操作

int QuePop(int *d)

{

//temp为要输出的元素

node *temp;

//因为temp可能为NULL,因此我们用P记录temp->next的值,后续会用到

node *p;

do

{

if(QueEmpty())

return 0;

temp = que.front->next;

if(temp!=NULL)

p=temp->next;

else

p=NULL;

}

while(!__sync_bool_compare_and_swap(&(que.front->next),temp,p));

//更新尾指针

__sync_bool_compare_and_swap(&(que.rear),temp,que.front);

if(temp!=NULL)

{

*d = temp->data;

free(temp);

return 1;

}

return 0;

}

//两个线程函数:一个入队,一个出队

void * thread_push(void *arg)

{

while(1)

{

int data = rand()%100;

QuePush(&data);

printf(“队列插入元素:%d\n”,data);

sleep(1);

}

}

void *thread_pop(void *arg)

{

int data;

while(1)

{

sleep(2);

if(!QuePop(&data))

printf(“队列为空\n”);

else

printf(“队列输出元素:%d\n”,data);

}

}

int main()

{

//初始化队列

QueInit(&que);

//创建两个线程

pthread_t id[2];

pthread_create(&id[0],NULL,thread_push,NULL);

pthread_create(&id[1],NULL,thread_pop,NULL);

//等待线程结束

pthread_join(id[0],NULL);

pthread_join(id[1],NULL);

//在这之后还因该删除队列回收内存

//删除队列不涉及多线程操作,不再赘述

return 0;

}

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/134202.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • Linux 常用网络指令介绍一

    Linux 常用网络指令介绍一

    2021年7月22日
    85
  • 最新、最全、最准确的手机号正则表达式

    最新、最全、最准确的手机号正则表达式更新到2018年1月,支持最新的166号段/***手机号码正则表达式*/privatestaticfinalStringPHONE_NUMBER_REG="^(13[0-9]|14[579]|15[0-3,5-9]|16[6]|17[0135678]|18[0-9]|19[89])\\d{8}$";

    2022年6月14日
    28
  • 什么叫做公网IP_是不是公网ip

    什么叫做公网IP_是不是公网ip由于公网ip资源匮乏,NAT(地址转换)技术被广泛应用。其好处就是让更多的电脑能够上网,缺点在于你自己搭建了一台服务器。想实现远程访问,如果服务器的地址如果是经过NAT转换后的地址,外网是无法访问到的。这就引出了公网ip和私网ip的概念,可以通过开头的数字来判断ip地址的类型,下面就给大家普及一下。由于私网ip地址仅限于局域网内使用,并且是可以重复的,所以IANA当初划分了一些网段,专供局域网内使用。具体网段如下:10.x.x.x192.168.x.x172.16.x.x-172.3

    2022年10月21日
    0
  • 信道带宽_信道和带宽的关系

    信道带宽_信道和带宽的关系信道包括模拟信道和数字信道。在模拟信道,带宽按照公式W=f2-f1计算;数字信道的带宽为信道能够达到的最大数据速率,两者可通过香农定理互相转换。码元:数字信号中每一个符号的通称。即可以用二进制表示,也可以用其它进制的数表示(例如二进制就是0和1表示两个符号,四进制就是0、1、2和3表示四个符号。四进制又可以用二进制表示,00、01、10、11)。码元传输速率,又称为码元速率或传码率。码元…

    2022年10月11日
    0
  • 每个人都应该是善良的

    每个人都应该是善良的

    2021年7月22日
    63
  • Ubuntu18.04 安装 gcc「建议收藏」

    Ubuntu18.04 安装 gcc「建议收藏」在Ubuntu18.04下安装gcc的指令:sudoadd-apt-repositoryppa:unbutu-toolchain-r/testsudoapt-getupdatesudoapt-getinstallgcc  这种方法最简单,默认安装最新版本的gcc,安装完成后,输入下面指令查看gcc的版本gcc-v    Refere…

    2022年7月24日
    28

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号