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

主线故事:

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

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

2 必须一块一块的存

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

设计模式:

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

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

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

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

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

运行环境:

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

进程持续1分钟以内

#define _GNU_SOURCE
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

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;
}

你可能感兴趣的:(开发语言,c语言,开源)