Linux中自旋锁实现的基本原理之经典

2023-05-16

目录

一、自旋锁基本简介

二、自旋锁和互斥锁之间的区别

三、自旋锁API函数

四、自旋锁代码实现

五、互斥锁实现

运行结果:

总结:


线程:线程是进程资源分配的最小单位,每个进程都有的自己的main(主线程)

线程同步:多个线程按顺序以此执行访问共享资源(数据)。

线程同步的必要性:防止多线程并发访问共享数据的时候出现数据混乱、不一致 的问题。

线程同步的方法:互斥锁、自旋锁、条件变量....

实例:创建两个线程轮流计数

 

#include<stdio.h>

#include<stdlib.h>

#include<string.h>

#include<unistd.h>

#include<pthread.h>

int q_cont;     //计数值

long loops;     //计数次数

void *task(void *arg)

{

    long num=(long)arg;

    int j_cont=0;

    int i;

    for(i=0;i<num;i++)  //单线程循环loops次

    {

        //计数

        j_cont=q_cont;

        j_cont++;

        q_cont=j_cont;

    }

    pthread_exit(NULL);

}

int main(int aegc,char *argv[])

{

    int i;

    pthread_t tid1,tid2;

    loops=atoi(argv[1]);

  

    //创建线程      

    pthread_create(&tid1,NULL,task,(void *)loops);

    pthread_create(&tid2,NULL,task,(void *)loops);

    //等待线程结束

    pthread_join(tid1,NULL);

    pthread_join(tid2,NULL);

    //打印次数

    printf("q_cont=%d\r\n",q_cont);

    return 0;

}

一、自旋锁基本简介

自旋锁本质上是一把锁,在访问共享资源之前对自旋锁进行上锁,在访问完成后释放自旋锁(解锁)

从实现方式上来说,互斥锁是基于自旋锁来实现的,所以自旋锁相比较于互斥锁更加底层。

     

二、自旋锁和互斥锁之间的区别

互斥锁

自旋锁

等待方式

阻塞(休眠)

自旋(不停申请)

效率

低(休眠、唤醒开销大)

应用场景

进程上下文

中断上下文

缺点

  1. 不能放在中断服务函数中,易锁死
  2. 开销大

自旋占用CPU资源,不适用于长时间等待

三、自旋锁API函数

1、pthread_spin_init() 初始化函数

原型:int pthread_spin_init(pthread_spinlock_t *lock ,int pshared);

参数:pthread_spinlock_t *lock pthread_spinlock_t 定义的锁

int pshared PTHREAD_PROCESS_PRIVATE

PTHREAD_PROCESS_SHARED

PTHREAD_PROCESS_PRIVATE  自旋锁只能在同一进程中的线程进行操作。

PTHREAD_PROCESS_SHARED  自旋锁可以由任何进程中的任何线程操作。

2、pthread_spin_lock(pthread_spinlock_t *lock) 加锁函数

3、pthread-spin_unlock(pthread_spinlock_t *lock) 解锁函数

4、pthread_spin_destroy(pthread_spinlock_t *lock) 摧毁锁函数

补充:

pthread_spin_trylock(pthread_spinlock_t *lock) 加锁函数

函数对自旋锁进行加锁,如果未能获取到锁,就立刻返回错误,错误码为 EBUSY。

互斥锁API函数

  1. pthread_mutex_init(pthread_mutex_t*mutex,constpthread_mutexattr_t mutexattr) 初始化函数

  1. Pthread_mutex_lock(pthread_mutex_t *mutex) 加锁函数

  1. Pthread_mutex_unlock(pthread_mutex_t *mutex) 解锁函数

4、Pthread_mutex_destroy(pthread_mutex_t *mutex) 摧毁锁

四、自旋锁代码实现

#include<stdio.h>

#include<stdlib.h>

#include<string.h>

#include<unistd.h>

#include<pthread.h>

int q_cont;         //计数值

long loops;         //循环计数次数

pthread_spinlock_t spin;

void *task(void *arg)

{

    long num=(long)arg;

    int j_cont=0;

    int i;

    for(i=0;i<num;i++)      //循环loops次

    {

        //加锁

        pthread_spin_lock(&spin);

        

        j_cont=q_cont;

        j_cont++;

        q_cont=j_cont;

        

        //解锁

        pthread_spin_unlock(&spin);

    }

    pthread_exit(NULL);

}

int main(int aegc,char *argv[])

{

    int i;

    pthread_t tid1,tid2;

    loops=atoi(argv[1]);

    

    //初始化自旋锁

    pthread_spin_init(&spin,PTHREAD_PROCESS_PRIVATE);

    //创建线程

    pthread_create(&tid1,NULL,task,(void *)loops);

    pthread_create(&tid2,NULL,task,(void *)loops);

    //等待线程

    pthread_join(tid1,NULL);

    pthread_join(tid2,NULL);

    

    //摧毁锁

    pthread_spin_destroy(&spin);

    printf("q_cont=%d\r\n",q_cont);

    return 0;

}

五、互斥锁实现

#include<stdio.h>

#include<stdlib.h>

#include<string.h>

#include<unistd.h>

#include<pthread.h>

int q_cont;     //计数值

long loops;     //计数次数

pthread_mutex_t lock;

void *task(void *arg)

{

    long num=(long)arg;

    int j_cont=0;

    int i;

    for(i=0;i<num;i++)  //单线程循环loops次

    {

        //加锁

        pthread_mutex_lock(&lock);

        

        //计数

        j_cont=q_cont;

        j_cont++;

        q_cont=j_cont;

        

        //解锁

        pthread_mutex_unlock(&lock);

    }

    pthread_exit(NULL);

}

int main(int aegc,char *argv[])

{

    int i;

    pthread_t tid1,tid2;

    loops=atoi(argv[1]);

    

    //互斥锁初始化

    pthread_mutex_init(&lock,NULL);

    

    //创建线程      

    pthread_create(&tid1,NULL,task,(void *)loops);

    pthread_create(&tid2,NULL,task,(void *)loops);

    //等待线程结束

    pthread_join(tid1,NULL);

    pthread_join(tid2,NULL);

    //摧毁锁

    pthread_mutex_destroy(&lock);

    

    //打印次数

    printf("q_cont=%d\r\n",q_cont);

    return 0;

}

运行结果:

不加锁:

加锁后:

总结:

将互斥锁替换为自旋锁之后,测试结果打印也是没有问题的,并且通过对比可以发现,替换为自旋锁之后,程序运行所耗费的时间明显变短了,说明自旋锁确实比互斥锁效率要高。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Linux中自旋锁实现的基本原理之经典 的相关文章

  • linux x86 汇编语言 sys_read 调用的第一个参数应为 0 (stdin)

    我正在编写一个简单的汇编程序来从标准输入读取 如 scanf 这是我的代码 section bss num resb 5 section txt global start start mov eax 3 sys read mov ebx 0
  • sleep 0 有特殊含义吗?

    我看到很多用法sleep 0在我的一个客户项目中 代码看起来像这样 while true sleep 0 end 阅读一些像这样的答案this https stackoverflow com questions 3727420 signif
  • 从多线程程序中调用 system()

    我们正在开发一个用 C 编写的多线程内存消耗应用程序 我们必须执行大量的 shellscript linux 命令 并获取返回码 读完之后article http www linuxprogrammingblog com threads a
  • 无需 cron 在后台发送邮件

    我想知道是否有一种方法可以运行 PHP 循环 以便在后台向订阅者发送几百封电子邮件 我的目标是格式化新闻通讯 单击发送 然后关闭浏览器或更改页面 当然 发送电子邮件的实际过程将在后台运行 不会因浏览器关闭而中断 我知道这可以通过 cron
  • 如何查找连接到 AF_INET 套接字的客户端的 UID?

    有什么方法或类似的东西ucred for AF UNIX如果是AF INET插座 TCP在我的例子中 找出连接到我的套接字的客户端的UID 还有 proc net tcp但它显示了UID of the creator插座的而不是连接的cli
  • 没有可用的符号表信息

    我正在测试第三方的库 它崩溃了 当我想查看崩溃的原因时 我的 gdb 告诉我没有可用的调试符号 Program received signal SIGSEGV Segmentation fault Switching to Thread 0
  • arm-linux-gnueabi 编译器选项

    我在用 ARM Linux gnueabi gcc在 Linux 中为 ARM 处理器编译 C 程序 但是 我不确定它编译的默认 ARM 模式是什么 例如 对于 C 代码 test c unsigned int main return 0x
  • 信号处理程序有单独的堆栈吗?

    信号处理程序是否有单独的堆栈 就像每个线程都有单独的堆栈一样 这是在 Linux C 环境中 来自 Linux 手册页signal 7 http kernel org doc man pages online pages man7 sign
  • 在 Linux 中禁用历史记录 [关闭]

    Closed 这个问题不符合堆栈溢出指南 help closed questions 目前不接受答案 要在 Linux 环境中禁用历史记录 我执行了以下命令 export HISTFILESIZE 0 export HISTSIZE 0 u
  • 为什么我收到的数据包数据大小大于mss?

    我在两台 PC 上使用 ifconfig ethX mtu 300 修改了 MTU 并使用 netperf 测试网络 我用 WireShark 嗅探了 SYN 数据包中的 MSS 260 但我得到了一些大于 260 的数据包 为什么 嗅探器
  • 为什么我收到“无法进行二进制日志记录”的信息。在我的 MySQL 服务器上?

    当我今天启动 MySQL 服务器并尝试使用以下命令进行一些更改时用于 MySQL 的 Toad http www quest com toad for mysql 我收到此消息 MySQL 数据库错误 无法进行二进制日志记录 消息 交易级别
  • 是否可以在Linux上将C转换为asm而不链接libc?

    测试平台为Linux 32位 但也欢迎 Windows 32 位上的某些解决方案 这是一个c代码片段 int a 0 printf d n a 如果我使用 gcc 生成汇编代码 gcc S test c 然后我会得到 movl 0 28 e
  • Bash 解析和 shell 扩展

    我对 bash 解析输入和执行扩展的方式感到困惑 对于输入来说 hello world 作为 bash 中的参数传递给显示其输入内容的脚本 我不太确定 Bash 如何解析它 Example var hello world displaywh
  • 强制卸载 NFS 安装目录 [关闭]

    Closed 这个问题不符合堆栈溢出指南 help closed questions 目前不接受答案 Locked 这个问题及其答案是locked help locked posts因为这个问题是题外话 但却具有历史意义 目前不接受新的答案
  • 跟踪 Linux 程序中活跃使用的内存

    我想跟踪各种程序在特定状态下接触了多少内存 例如 假设我有一个图形程序 最小化时 它可能会使用更少的内存 因为它不会重新绘制窗口 这需要读取图像和字体并执行大量库函数 这些对象仍然可以在内存中访问 但实际上并没有被使用 类似的工具top它们
  • 如何检测并找出程序是否陷入死锁?

    这是一道面试题 如何检测并确定程序是否陷入死锁 是否有一些工具可用于在 Linux Unix 系统上执行此操作 我的想法 如果程序没有任何进展并且其状态为运行 则为死锁 但是 其他原因也可能导致此问题 开源工具有valgrind halgr
  • 通过特定分隔符删除字符串

    我的文件中有几列 其中第二列有 分隔符 我想删除第二列中的第一个 第三个和第四个字符串 并将第二个字符串留在该列中 但我有正常的分隔符空间 所以我不知道 input 22 16050075 A G 16050075 A G 22 16050
  • 使用 grep 查找包含所有搜索字符串的行

    我有一个文件 其中包含很多与此类似的行 id 2796 some model Profile message type MODEL SAVE fields account 14 address null modification times
  • GLIBCXX_3.4.26 未找到在 BeagleBone 上运行交叉编译的程序

    我有以下程序 include
  • 如何在 shell 脚本中并行运行多个实例以提高时间效率[重复]

    这个问题在这里已经有答案了 我正在使用 shell 脚本 它读取 16000 行的输入文件 运行该脚本需要8个多小时 我需要减少它 所以我将其划分为 8 个实例并读取数据 其中我使用 for 循环迭代 8 个文件 并在其中使用 while

随机推荐