C- 使用原子变量实现自旋锁

自旋锁

自旋锁(Spinlock)是一种常用于多线程编程中的低开销锁,其特点是当线程尝试获取锁而锁已被其他线程占用时,该线程会处于一个持续的忙等待(busy-wait)状态,直到它可以获取到锁为止。这种方法避免了线程切换和上下文切换的开销,但是如果锁被持有的时间较长,它可能会造成CPU时间的浪费。

自旋锁最适用于以下经典场景:

  1. 短临界区:当需要保护的代码执行非常快,锁的持有时间非常短时,自旋锁是非常有效的。

  2. 低竞争:当很少有线程试图同一时间获取锁时,自旋锁是有用的。

  3. 实时系统:在某些实时系统中,线程切换和上下文切换的代价可能非常高,自旋锁可能是一个更好的选择,因为它们可以确保线程在短时间内完成其工作。

  4. 无法睡眠的环境:在某些环境中,如内核中断处理程序或其他不允许睡眠的环境中,使用自旋锁可能是唯一的同步选项。

  5. 多核和多处理器系统:在多核和多处理器的系统中,当一个线程在一个核上忙等待,另一个线程可能在另一个核上释放锁,这种情况下,自旋锁可能比其他类型的锁更高效。

然而,值得注意的是,如果锁可能被持有很长时间或有高竞争的情况,自旋锁可能不是一个好的选择,因为它可能导致大量的CPU时间浪费。在这种情况下,其他的锁机制,如互斥锁或读写锁,可能是更好的选择。

实现自旋锁

使用原子变量实现自旋锁涉及到利用原子操作来确保锁的原子性。以下是使用C11中的stdatomic.h来实现一个简单的自旋锁:

#include <stdio.h>
#include <stdatomic.h>
#include <pthread.h>typedef struct {atomic_flag flag;
} spinlock_t;void spinlock_init(spinlock_t *lock) {atomic_flag_clear(&lock->flag);
}void spinlock_lock(spinlock_t *lock) {while (atomic_flag_test_and_set(&lock->flag));
}void spinlock_unlock(spinlock_t *lock) {atomic_flag_clear(&lock->flag);
}spinlock_t lock;
int shared_data = 0;void *worker(void *arg) {for (int i = 0; i < 100000; ++ i) {spinlock_lock(&lock);shared_data ++;spinlock_unlock(&lock);}pthread_exit(0);
}int main() {pthread_t t1, t2;spinlock_init(&lock);pthread_create(&t1, NULL, worker, NULL);pthread_create(&t2, NULL, worker, NULL);pthread_join(t1, NULL);pthread_join(t2, NULL);printf("Shared data: %d\n", shared_data);pthread_exit(0);
}

这里的关键是atomic_flag_test_and_set函数,它检查atomic_flag的当前值,如果它是false(即锁未被持有),则设置它为true(锁被持有)并返回原始值。这个操作是原子的,意味着在多线程环境中,只有一个线程能够成功地将flag设置为true。其他尝试获取锁的线程将会在while循环中自旋,直到锁被释放。

在 C11 标准中,atomic_flag的默认/初始状态是未设置(即其值为false)。当使用atomic_flag_clear()函数时,会将其设置回这个未设置状态。

但要注意,当使用atomic_flag变量时,为了确保其正确初始化,应该使用宏ATOMIC_FLAG_INIT。例如:

spinlock_t mylock = { ATOMIC_FLAG_INIT };

在上述自旋锁示例中,我们使用了atomic_flag_clear()函数来确保在初始化后标志是清除的(即设置为false)。


补充

atomic_flag

atomic_flag是C11标准中定义的一种原子类型,用于实现原子标记。它是原子操作库中最基础的组件,并且是确保原子性的最简单工具。原子类型的目的是在多线程环境中提供对单个数据的无锁访问,以确保数据操作的原子性。

以下是关于atomic_flag的一些关键点:

  1. 简单性atomic_flag只有两个可能的状态:设置(true)和未设置(false)。

  2. 原子性:操作atomic_flag的函数都是原子的,这意味着在多线程环境中对其进行的操作都是不可分割的。当一个线程在操作它时,其他线程无法干扰这个操作。

  3. 操作

    • atomic_flag_test_and_set:这个函数检查atomic_flag的当前状态。如果它未被设置,函数会设置它并返回先前的值。这个函数通常用于尝试获取锁。
    • atomic_flag_clear:这个函数将atomic_flag重置为未设置状态。这个函数通常用于释放锁。
  4. 初始化:要正确初始化atomic_flag,应使用ATOMIC_FLAG_INIT宏。

  5. 用途:由于其简单性,atomic_flag通常用于构建更复杂的同步原语,如自旋锁。

  6. 无锁保证atomic_flag提供了锁自由的保证。这意味着在其操作中不存在可能导致线程阻塞的锁。这是利用硬件提供的原子操作来实现的。

总的来说,atomic_flag是一个低级的同步原语,通常用于构建高级的同步工具或数据结构。尽管它看起来简单,但其提供的原子性保证使其在并发编程中非常有价值。

atomic_flag_clear()

atomic_flag_clear() 是 C11 和 C++11(及更高版本)中的原子操作库的一部分,专门用于操作 atomic_flag 类型。它的主要作用是将 atomic_flag 对象的状态重置为“清除”状态。

以下是关于 atomic_flag_clear() 的详细介绍:

  1. 函数签名:

    void atomic_flag_clear(volatile atomic_flag *obj);
    void atomic_flag_clear(atomic_flag *obj);
    
  2. 参数:

    • obj: 指向要清除的 atomic_flag 对象的指针。
  3. 返回值:

    • 这个函数没有返回值。
  4. 功能:

    • atomic_flag_clear() 函数将给定的 atomic_flag 对象的状态设置为清除状态(也就是 false 或未设置状态)。
  5. 原子性:

    • atomic_flag_clear() 函数的操作是原子的,这意味着它是不可分割的。当一个线程调用此函数来清除标记时,其他线程无法看到这个操作的任何中间状态。这确保了对该标记的所有操作都是线程安全的。
  6. 常见用途:

    • 通常,在使用 atomic_flag 作为自旋锁的基础时,当线程释放锁时,它会调用 atomic_flag_clear() 来标记锁为可用。
  7. 注意事项:

    • 在调用 atomic_flag_clear() 之前,通常会有一些其他的原子检查或操作来确定标记是否已经被设置,以确保正确的并发行为。

总之,atomic_flag_clear() 是一个基础的原子操作,它为更复杂的并发控制结构(如自旋锁)提供了基本的线程安全释放机制。

atomic_flag_test_and_set()

atomic_flag_test_and_set() 是 C11 和 C++11 标准(及更高版本)中的原子操作库的一部分,主要用于 atomic_flag 类型。这个函数的主要功能是测试 atomic_flag 的当前状态,并将其设置为 true。这一操作是原子的,确保在多线程环境下其行为是一致的和预期的。

以下是关于 atomic_flag_test_and_set() 的详细介绍:

  1. 函数签名:

    bool atomic_flag_test_and_set(volatile atomic_flag *obj);
    bool atomic_flag_test_and_set(atomic_flag *obj);
    

    在 C++ 中,还有一个额外的重载版本,支持 memory_order 参数,允许指定该操作的内存序语义。

  2. 参数:

    • obj: 指向要测试和设置的 atomic_flag 对象的指针。
  3. 返回值:

    • 如果 atomic_flag 之前的状态是已设置(true),则返回 true;否则返回 false
  4. 功能:

    • atomic_flag_test_and_set() 函数首先检查给定的 atomic_flag 对象的状态。
    • 如果其状态为未设置(false),则将其设置为 true 并返回 false
    • 如果其状态已经是设置的(true),则保持其状态并返回 true
  5. 原子性:

    • atomic_flag_test_and_set() 函数的操作是原子的。这意味着当一个线程正在执行此函数时,其他线程无法干扰或看到这个操作的任何中间状态。这是并发编程中非常关键的特性,尤其是在实现如自旋锁这样的并发控制结构时。
  6. 常见用途:

    • atomic_flag_test_and_set() 常被用作基于 atomic_flag 的自旋锁的核心。如果一个线程尝试获取锁(通过调用 atomic_flag_test_and_set())并且返回 false,这意味着该线程成功获取了锁。如果返回 true,则锁已经被其他线程持有,因此该线程必须等待或尝试其他操作。
  7. 注意事项:

    • 当使用 atomic_flag_test_and_set() 函数时,应该始终确保对应的 atomic_flag 在使用前已经被清除(使用 atomic_flag_clear())。

总之,atomic_flag_test_and_set() 提供了一种在多线程环境中原子地测试和设置标志的方法,它是许多并发控制结构的基础。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/111649.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

哈夫曼树的建立(C++,最优树)

介绍&#xff1a; 哈夫曼树&#xff08;Huffman Tree&#xff09;是一种用于数据压缩的树形数据结构。它是由刚特哈夫曼于1952年发明的。 哈夫曼树的特点是&#xff1a;对于一个长度为n的字符集&#xff0c;它可以将每个字符在树上表示为一个唯一的二进制编码。在哈夫曼树中&am…

NSSCTF做题(10)

叫10好听一点&#xff0c;就是补9的 第7页的内容 [SWPUCTF 2022 新生赛]ez_sql get传参说是不安全&#xff0c;那就只能用post了 有回显了&#xff0c;两个假的flag 发现万能密码 1 or 11#变成了 11# 11# 1 11#1# 11# 11# 发现or和空格都无了&#xff0c;union也过滤 …

golang的json转pb验证

基于这篇文章的最后一个代码进行验证。 https://blog.csdn.net/mijichui2153/article/details/133894403?csdn_share_tail%7B%22type%22%3A%22blog%22%2C%22rType%22%3A%22article%22%2C%22rId%22%3A%22133894403%22%2C%22source%22%3A%22mijichui2153%22%7D 1、准备 &…

Python新手常犯的8个错误,你中招了吗?

我们都知道Python是一门非常流行和强大的编程语言&#xff0c;而作为一个刚入门Python编程的新手&#xff0c;你可能会犯一些常见的错误&#xff0c;这些错误可能会阻碍你的学习和项目进展。 如何避免这些常见的错误呢&#xff1f;在本文中&#xff0c;小编将为你介绍8个新手P…

OJ第四篇

文章目录 链表分割环形链表有效的括号 链表分割 链接: 链表分割 虽然这个题牛客网中只有C,但是无所谓&#xff0c;我们只要知道C是兼容C的就可以了 至于说这个题的思路&#xff0c;我们就弄两个链表&#xff0c;把小于x的结点放到一个链表中&#xff0c;剩下的放到另一个链表…

群狼调研(长沙社情民意调查) | 残疾人服务满意度调查流程

以下是一个基本的残疾人服务满意度调查流程&#xff0c;可以根据实际情况进行调整和修改&#xff1a; 1. 制定调查目标和范围&#xff1a;明确调查的目标、范围和重点&#xff0c;确定需要评估的服务类型和方面。 2. 制定调查计划&#xff1a;确定调查的时间表、预算、人员分…

【c#】Quartz开源任务调度框架学习及练习Demo

Quartz开源任务调度框架学习及练习Demo 1、定义、作用 2、原理 3、使用步骤 4、使用场景 5、Demo代码参考示例 6、注意事项 7、一些Trigger属性说明 1、定义、作用 Quartz是一个开源的任务调度框架&#xff0c;作用是支持开发人员可以定时处理业务&#xff0c;比如定时…

Python常用视频编辑操作——读取与保存视频、更改帧数、拼接视频、视频语音合并、视频与图像互转等

1.更改视频帧数 降低视频帧数&#xff0c;简单的操作只能降低视频帧数&#xff0c;如果要增加视频帧数&#xff0c;那就要使用深度学习进行插帧处理&#xff1a; import cv2 from moviepy.editor import * def change_fps(inpt_path,output_path,fps):# 加载视频video Video…

python实现TCPclient

python实现TCPclient是一件简单的事情&#xff0c;只要通过socket这个模块就可以实现。 一、实现步骤 1、导入模块&#xff1a; 首先&#xff0c;你需要导入Python的socket模块。 import socket2、创建Socket对象&#xff1a; 使用socket.socket()函数创建一个新的socket对…

Flink学习---15、FlinkCDC(CDC介绍、案例实操)

星光下的赶路人star的个人主页 未来总是藏在迷雾中让人胆怯&#xff0c;但当你踏入其中&#xff0c;便会云开雾散 文章目录 1、CDC简介1.1 什么是CDC1.2 CDC的种类1.3 Flink-CDC 2、FlinkCDC案例实操2.1 开启MySQL Binlog并重启MySQL2.2 FlinkSQL方式的应用2.2.1 导入依赖2.2.2…

Elasticsearch2.x Doc values

文档地址&#xff1a; https://www.elastic.co/guide/en/elasticsearch/reference/2.4/doc-values.html https://www.elastic.co/guide/en/elasticsearch/guide/2.x/docvalues-intro.html https://www.elastic.co/guide/en/elasticsearch/guide/2.x/docvalues.html https://ww…

【Java 进阶篇】HTML DOM 事件详解

当用户在网页上点击按钮、输入文本、鼠标移动到某个区域或执行其他互动操作时&#xff0c;这些动作都可以触发事件。HTML DOM&#xff08;文档对象模型&#xff09;允许我们使用JavaScript来捕获、处理和响应这些事件&#xff0c;以实现网页的交互和动态性。本篇博客将围绕HTML…

iOS原生、Android 原生, flutter 三种方式给照片流添加文字(水印)

效果图:三中代码实现的效果差不多 Swift:代码 import UIKitclass ImageWatermarking: NSObject {static func textToImage(drawText text: String, inImage initImage: UIImage, atPoint point: CGPoint) -> UIImage {let textColor = UIColor.whitelet textFont = UIFon…

ccpc2023秦皇岛F. Mystery of Prime(dp)

题目要求改变数组中的数字使相邻数字之和是质数&#xff0c;同时改变数字的次数最少 因为改变的数字可以无穷大 我假设当一个数改变为一个某一个偶数时&#xff0c;他周围的任意的奇数肯定能和他相加变成质数 当一个数变为某一个大于1的奇数时&#xff0c;他周围任意偶数肯定…

港联证券:资金融通构成强支撑 “一带一路”金融合作开新局

本年是共建“一带一路”主张提出十周年。经过十年打开&#xff0c;共建“一带一路”从夯基垒台、立柱架梁到落地生根、持久打开&#xff0c;已成为打开包容、互利互惠、协作共赢的国际协作途径。“资金融通”作为首份“一带一路”白皮书提出的“五通”之一&#xff0c;定位为“…

DPDK收发包流程分析

一、 前言 DPDK是intel工程师开发的一款用来快速处理数据包的框架,最初的目的是为了证明传统网络数据包处理性能低不是intel处理器导致的,而是传统数据的处理流程导致,后来随着dpdk的开源及其生态的快速发展,dpdk成为了高性能网络数据处理的优秀框架。本篇文章主要介绍DPDK…

Django实现音乐网站 ⒇

使用Python Django框架做一个音乐网站&#xff0c; 本篇音乐播放器-添加播放音乐功能实现。 目录 创建播放器数据表 设置表结构 执行创建表 命令 执行 数据表结构 添加单个歌曲 创建路由 加入播放器视图 模板处理 基类方法 子页面调用 优化弹窗 加入layui文件 基…

在URP管线中添加ShaderMaterial自定义GUI的方法

编写GUI面板 1. 新建GUI子面板 using UnityEngine; using UnityEngine.Rendering;namespace UnityEditor.Rendering.Universal.ShaderGUI {internal class CP_XXXOutLineGUI{public static class Styles{}public struct LitProperties{public LitProperties(MaterialProperty…

vxe-table添加排序

在 <vxe-column> 中添加sortable属性 <vxe-columnfield"longitude"sortabletitle"经度"></vxe-column><vxe-columnfield"latitude"sortabletitle"纬度"></vxe-column><vxe-columnfield"dateEst…

Cross decomposition交叉分解大比拼:性能、应用场景和可视化对比总结

交叉分解(Cross Decomposition)在机器学习中是一种用于分析两组变量之间关系的技术。它能够找出两组数据之间的线性关系,并将这些关系用于预测或分类。在本文中将探讨四种不同的交叉分解方法:CCA(典型相关分析)、PLSCanonical、PLSRegression和PLSSVD,并从五个方面进行详…