C语言pthread互斥锁(mutex)和可重入锁(递归锁recursive)的演示

实验理论参考:

1 一旦共享资源被互斥锁锁定,则其余线程想访问共享资源必须等待,直到锁被释放

2 使用normal属性的互斥锁,一旦发生重入逻辑,则阻塞,成为死锁

   需要将属性改为recursive 成为可重入的,递归的

代码功能:

1 命令行传参 1 model=1

   演示异步未上锁之乱序

   演示count在数据竞态(Race Condition)下的错误值

2 命令行传参 2 model=2

   演示使用互斥锁后 线程的执行顺序

   演示count获得了正确值

   演示未使用可重入锁对资源的重入访问

3 命令行传参 3 model=3

   使用mutex的属性,开启递归锁(可重入锁),解决同一线程可重入问题

4 演示线程创建有序 执行无序 结束无序 回收有序 的深层逻辑

运行环境:

unix-like操作系统 gnu_c或兼容c库

编译后,命令行传参运行


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

// 全局变量 用于改变代码功能
int model = 0;
// 全局变量 用于演示同步异步
int count = 0;
// 全局都能看见 属于标准操作
pthread_mutex_t mutex_lock;
// 结构体 通常在全局声明 便于传参
struct args
{
    FILE *arg_fp;
    char arg_char;
};

void recursive(int a)
{
    // 锁递归函数的全部逻辑
    pthread_mutex_lock(&mutex_lock);
    if (a != 0)
    {
        printf("%lu recursive %d\n", pthread_self(), a--);
        recursive(a);
    }
    pthread_mutex_unlock(&mutex_lock);
}
// 每个线程的逻辑
void *put_char(void *p)
{
    printf("%lu start \n", pthread_self());
    int i;
    char c;
    FILE *fp;
    // 要打印的大写字母赋值,打开的fp赋值
    if (p != NULL)
    {
        struct args *ptr = (struct args *)p;
        c = ptr->arg_char;
        fp = ptr->arg_fp;
    }
    // 上锁,检查锁没锁上
    if (model == 1)
    {
    }
    else if (model == 2)
    {
        int r = pthread_mutex_lock(&mutex_lock);
        if (r != 0)
        {
            fprintf(stderr, "pthread_mutex_lock:%s", strerror(r));
        }
    }
    else
    {
        int r = pthread_mutex_lock(&mutex_lock);
        if (r != 0)
        {
            fprintf(stderr, "pthread_mutex_lock:%s", strerror(r));
        }
    }

    // 主逻辑就是每个线程 向文件f1输出2W次大写字母采用a+模式,全局变量增加2w次,每次+1
    for (i = 0; i < 20000; i++)
    {
        fprintf(fp, "%c", c);
        count++;
    }
    // 解锁
    if (model == 1)
    {
    }
    else if (model == 2)
    {
        pthread_mutex_unlock(&mutex_lock);
    }
    else
    {
        pthread_mutex_unlock(&mutex_lock);
    }
    printf("%lu end \n", pthread_self());
    // 退出什么也不传递
    pthread_exit(NULL);
}
void mutex_init()
{
    pthread_mutexattr_t attr;
    pthread_mutexattr_init(&attr);
    if (model == 2)
    {
        // 此选项用于正常未修改的互斥锁
        // 在执行递归任务时,会死锁,阻塞
        pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_NORMAL);
    }
    else if (model == 3)
    {
        // 开启此选项,使用递归锁(可重入锁),在执行递归任务时,同一线程可以无限重入被锁定的函数
        pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
    }
    else
    {
        pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
    }
    pthread_mutex_init(&mutex_lock, &attr);
}
// 初始化5个线程
void five_thread_init(FILE *fp)
{
    //={0}是为了清垃圾
    pthread_t tids[5] = {0};
    // 这是传给每个线程的参数
    char tid_argv[5] = {'A', 'B', 'C', 'D', 'E'};
    // 将两个参数封装在struct里 传给每个线程
    struct args arg_s[5] = {0};

    for (size_t i = 0; i < 5; i++)
    {
        arg_s[i].arg_fp = fp;
        arg_s[i].arg_char = tid_argv[i];
        // 第一个参数是线程id,pthread_create函数返回就被存入tids[i]
        // 第二个参数是线程属性,啥也不改填null
        // 第三个参数是线程要执行的函数,此函数具有标准函数指针,必须符合
        // 第四个参数是要传给这个执行函数的参数
        pthread_create(&tids[i], NULL, put_char, (void *)&arg_s[i]);
    }
    for (size_t i = 0; i < 5; i++)
    {
        // 结束无序 回收有序
        // 结束的线程变成僵尸线程等待有序回收
        pthread_join(tids[i], NULL);
    }
}
void is123(int argc, char *argv1)
{
    if (argc != 2)
    {
        printf("Exit!\n");
        _exit(EXIT_FAILURE);
    }
    else
    {
        if (strcmp(argv1, "1") == 0 ||
            strcmp(argv1, "2") == 0 ||
            strcmp(argv1, "3") == 0)
        {
            model = atoi(argv1);
        }
        else
        {
            printf("Exit!\n");
            _exit(EXIT_FAILURE);
        }
    }
}
int main(int argc, char *argv[])
{

    is123(argc, argv[1]);
    // 进程开始 出发!
    printf("%lu main start\n", pthread_self());
    // 如果存在共享资源文件f1,则删除
    if (access("f1", F_OK) == 0)
    {
        remove("f1");
    }
    // 初始化一个多类型的互斥锁(normal,recursive)
    mutex_init();
    // 打开f1文件,fprintf使用的是字符,便于观察,所以选用fopen
    FILE *fp = fopen("f1", "a+");
    // 初始化5个线程
    five_thread_init(fp);
    // 关闭f1
    fclose(fp);
    // 这句话用来展示同步异步的不同,理论10W,异步竞态条件下不满10W
    printf("count_value: %d\n", count);
    // pthread_join会阻塞到线程全跑完 在pthread_join之后复用mutex_lock现在锁是空闲的
    // 运行到此处可以认为这是一把新锁
    // 递归函数用于演示递归锁(可重入锁)
    if (model == 1)
    {
    }
    else if (model == 2)
    {
        recursive(10);
    }
    else
    {
        recursive(10);
    }
    // 玩完销毁,不留痕迹
    pthread_mutex_destroy(&mutex_lock);

    // 全剧终
    printf("%lu main end\n", pthread_self());
    return 0;
}

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