C语言演示多线程编程条件下自旋锁和屏障的使用

news/2024/7/10 20:26:41 标签: 开发语言, c语言, 开源

主线故事:

有4个人玩游戏输了,惩罚:

1 分别使用4台不同的ATM机给我存钱

2 必须一块一块的存

3 存完还得在ATM上看一下我的余额

设计模式:

1 每个人使用一条单独的线程,再准备一个计时线程用来输出时间

2 存钱 涉及到 对共享资源的读写,是原子操作需要用锁保护 这里使用自旋锁

3 都存完钱后需要等待 在各自的ATM上回显余额 这里使用屏障技术

4 如果在主线程中回显 对应他们给我打电话告诉我存完了 我自己看一下 则不需要使用屏障

   因为  join保证了 我查看是在所有操作都完成的情况下进行的

运行环境:

unix-like系统 或 GNU_C库 或 同等条件 可以在IDE集成环境下也可以编译后在终端单独运行

进程持续1分钟以内

#define _GNU_SOURCE
#include <pthread.h>
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>
#include <string.h>
#include <stdlib.h>
#include <sys/msg.h>
#include <time.h>

pthread_spinlock_t spinlock;
pthread_barrier_t barrier;
// 这是被cancel的时间显示线程的清理函数
void cleanup_func(void *p)
{
    printf("%ld pthread_exit\n", pthread_self());
}
// 用于显示时间的线程
void *get_time(void *p)
{
    // 注册清理函数
    pthread_cleanup_push(cleanup_func, NULL);
    // 每隔1秒显示当前时间
    while (1)
    {
        time_t rawtime;
        struct tm *info;
        char buffer[128];
        time(&rawtime);
        info = localtime(&rawtime);
        strftime(buffer, 128, "%H:%M:%S", info);
        printf("%s\n", buffer);
        sleep(1);
    }
    pthread_cleanup_pop(1);
    pthread_exit(NULL);
}
// 用于回显余额
void balance_echo()
{
    char buf[128] = {0};
    // 打开
    int fd = open("account_balance", O_RDWR);
    // 读
    pread(fd, buf, 128, 0);
    // 打印在stdout
    printf("卡内余额:$%s\n", buf);
    // 关闭
    close(fd);
}
// 用于处理每台ATM不同的存钱逻辑
void atm_handler(int serial_number)
{
    char buf[128] = {0};
    int fd = open("account_balance", O_RDWR);
    // 储蓄序列号乘以500000的钱
    int count = serial_number * 500000;
    // 上自旋锁
    pthread_spin_lock(&spinlock);
    // 读取余额
    pread(fd, buf, 128, 0);
    // 字符串转整数
    long long ab = strtoll(buf, NULL, 10);

    for (size_t i = 0; i < count; i++)
    {
        // 一块一块加,测试自旋锁是不是好使
        ab += 1;
        // 写入buf,整数转字符串
        snprintf(buf, 128, "%lld", ab);
        // 写入余额文件
        pwrite(fd, buf, strlen(buf), 0);
    }
    // 解自旋锁
    pthread_spin_unlock(&spinlock);
    close(fd);
}
void *spinlock_in_barrier(void *p)
{
    // void*标准转整 便于使用
    int num = *((int *)p);
    // 调用ATM储蓄逻辑
    atm_handler(num);
    // 当某线程到达屏障点时输出
    printf("%ld 到达了屏障点\n", pthread_self());
    // 等4个储蓄线程全到
    pthread_barrier_wait(&barrier);
    // 每个线程在自己的ATM显示屏上查询余额,而不是在主线程中
    balance_echo();
    // 线程结束
    printf("%ld pthread_exit\n", pthread_self());
    pthread_exit(NULL);
}

void account_balance_init()
{
    // 如果该文件存在就删除
    if (access("account_balance", F_OK) == 0)
    {
        remove("account_balance");
    }
    // 创建文件且有读写权限
    int fd = open("account_balance", O_RDWR | O_CREAT, 0700);
    // 卡内初始余额就是5块钱
    char buf[8] = "5";
    write(fd, buf, strlen(buf));
    close(fd);
}
// 创建1个计时线程,4个ATM线程并回收他们
void create_join_pthread()
{
    pthread_t tids[5] = {0};
    int i;
    // ATM的序列号,表示不同的ATM,存不同数额的钱
    int arr[5] = {0, 2, 4, 6, 8};
    pthread_create(&tids[0], NULL, get_time, NULL);
    for (i = 1; i < 5; i++)
    {
        pthread_create(&tids[i], NULL, spinlock_in_barrier, &arr[i]);
    }
    for (i = 1; i < 5; i++)
    {
        pthread_join(tids[i], NULL);
        printf("%lu join\n", tids[i]);
    }
    // 当4条ATM线程都返回时,回收计时线程
    if (pthread_cancel(tids[0]))
    {
        perror("cancel");
    }
    pthread_join(tids[0], NULL);
    printf("%lu join\n", tids[0]);
}
int main()
{
    // 初始化账户余额文件 ,设为5块钱
    account_balance_init();
    // 初始化自旋锁
    pthread_spin_init(&spinlock, PTHREAD_PROCESS_PRIVATE);
    // 初始化屏障,设为需要触发4次
    pthread_barrier_init(&barrier, NULL, 4);
    // 创建及回收5条线程
    create_join_pthread();
    // 销毁自旋锁
    pthread_spin_destroy(&spinlock);
    // 销毁屏障
    pthread_barrier_destroy(&barrier);
    return 0;
}


http://www.niftyadmin.cn/n/5422375.html

相关文章

Springboot+vue的疫情管理系统(有报告)。Javaee项目,springboot vue前后端分离项目。

演示视频&#xff1a; Springbootvue的疫情管理系统&#xff08;有报告&#xff09;。Javaee项目&#xff0c;springboot vue前后端分离项目。 项目介绍&#xff1a; 采用M&#xff08;model&#xff09;V&#xff08;view&#xff09;C&#xff08;controller&#xff09;三层…

H ive 的并行能力比Spark SQL弱么?

直接比较Hive和Spark SQL的并行能力并不是简单的任务&#xff0c;因为它们有着不同的架构和设计目标。以下是一些相关方面的考虑&#xff1a; 架构差异&#xff1a; Hive&#xff1a; Hive 是基于Hadoop MapReduce 的数据仓库工具&#xff0c;其执行查询的方式是通过将 Hive 查…

第一章 Pygame 游戏模块学习入门之简单基础学习

系列文章目录 第一章 Pygame 游戏模块学习入门之简单基础学习 第二章 Pygame 游戏模块学习入门之pygame精灵 第三章 Pygame 游戏模块学习入门之飞机大战 第四章 Pygame 游戏模块学习进阶之飞机大战&#xff08;复杂版&#xff09; 文章目录 系列文章目录前言一、pygame是什…

Neo4j 批量导入数据 从官方文档学习LOAD CSV 命令 小白可食用版

学习LOAD CSV&#x1f680; 在使用Neo4j进行大量数据导入的时候&#xff0c;发现如果用代码自动一行一行的导入效率过低&#xff0c;因此明白了为什么需要用到批量导入功能&#xff0c;在Neo4j中允许批量导入CSV文件格式&#xff0c;刚开始从网上的中看了各种半残的博客或者视频…

SpringBoot注解--08--注解@JsonInclude

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 JsonInclude注解是jackSon中最常用的注解之一&#xff0c;是为实体类在接口序列化返回值时增加规则的注解 1.JsonInclude用法2.JsonInclude注解中的规则有 案例需求…

【Java】获取手机文件名称补充

本地的 ADB 工具路径指的是你电脑上安装的 Android Debug Bridge&#xff08;ADB&#xff09;工具的路径。ADB 是 Android SDK 中的一个工具&#xff0c;用于与连接到计算机上的 Android 设备进行通信。你需要确保 ADB 已正确安装&#xff0c;并知道其在你计算机上的位置。 通…

软考笔记--层次式架构之表现层框架设计

一.表现层设计模式 1.MVC模式 MVC是一种目前广泛流行的软件设计模式。MVC强制性地把一个应用的输入、处理、输出流程按照视图、控制、模型的方式进行分离&#xff0c;形成了控制器&#xff0c;模型、视图三个核心模块。 (1)控制器&#xff08;Controller&#xff09;:接受用…

VSCode单机活动栏图标无法收起

如果活动栏为展开状态&#xff0c;单击活动栏图标可以正常收起&#xff0c;但无法通过再次单击打开&#xff0c;解决方案如下&#xff1a; 设置->工作台->外观&#xff1a; Activity Bar:Icon Click Behavior: 切换为默认的toggle