• C语言线程安全问题


    线程安全问题

    #include <stdio.h>
    #include <tinycthread.h>
    #include <io_utils.h>
    
    int count = 0;
    int Counter(void*arg)
    {
        for(int i = 0;i<100000;i++)
        {
            count++;
            /*
             * int temp = count;
             * count=temp+1;
             * return temp;
             * */
        }
        return 0;
    }
    
    int main()
    {
        thrd_t t1;
        thrd_t t2;
    
        thrd_create(&t1,Counter,NULL);
        thrd_create(&t2,Counter,NULL);
    
        thrd_join(t1,NULL);
        thrd_join(t2,NULL);
        PRINT_INT(count);
    
        return 0;
    }
    
    • 运行结果不是所要值原因是count++在并发时产生冲突

    线程安全的产生

    • 对共享资源进行非原子的访问
    • 不同线程之间代码可见性问题
    • 线程内部代码编译时的重排序问题

    解决方法一 消除副作用

    #include <stdio.h>
    #include <tinycthread.h>
    #include <io_utils.h>
    
    
    int Counter(void*arg)
    {
        int count = 0;
        for(int i = 0;i<100000;i++)
        {
            count++;
        }
        return count;
    }
    
    int main()
    {
        thrd_t t1;
        thrd_t t2;
    
        thrd_create(&t1,Counter,NULL);
        thrd_create(&t2,Counter,NULL);
        int count = 0;
        int result = 0;
    
        thrd_join(t1,&result);
        count+=result;
    
        thrd_join(t2,&result);
        count+=result;
    
        PRINT_INT(count);
    
        return 0;
    }
    

    解决方法二 原子类型

    #include<stdio.h>
    #include <tinycthread.h>
    #include <io_utils.h>
    #include <stdatomic.h>
    
    atomic_int count = 0;   //设置原子类型
    int Counter(void*arg)
    {
        for(int i = 0;i<100000;i++)
        {
            count++;
        }
        return 0;
    }
    
    int main()
    {
        thrd_t t1;
        thrd_t t2;
    
        thrd_create(&t1,Counter,NULL);
        thrd_create(&t2,Counter,NULL);
    
        thrd_join(t1,NULL);
        thrd_join(t2,NULL);
        PRINT_INT(count);
    
        return 0;
    
    }
    

    解决方法三 原子操作

    #include<stdio.h>
    #include <tinycthread.h>
    #include <io_utils.h>
    #include <stdatomic.h>
    
    atomic_flag resume_flag = ATOMIC_FLAG_INIT;
    int PrintNumber(void*arg)
    {
        int current = 0;
        while(atomic_flag_test_and_set(&resume_flag))
        {
            current++;
            PRINT_INT(current);
            thrd_sleep(&(struct timespec){.tv_sec=1},NULL);
        }
        return current;
    }
    int main()
    {
        atomic_flag_test_and_set(&resume_flag);
        thrd_t t;
        thrd_create(&t,PrintNumber,NULL);
        thrd_sleep(&(struct timespec){.tv_sec=5},NULL);
        atomic_flag_clear(&resume_flag);
    
        int last_number = 0;
        thrd_join(t,&last_number);
        PRINT_INT(last_number); 
    
        return 0;
    }
    

    解决方法四 锁

    #include <stdio.h>
    #include <tinycthread.h>
    #include <io_utils.h>
    
    int count = 0;
    mtx_t mutex;
    int Counter(void*arg)
    {
        for(int i = 0;i<100000;i++)
        {
            mtx_lock(&mutex);
            count++;
            mtx_unlock(&mutex);
            /*
             * int temp = count;
             * count=temp+1;
             * return temp;
             * */
        }
        return 0;
    }
    
    int main()
    {
        mtx_init(&mutex,mtx_plain);
        thrd_t t1;
        thrd_t t2;
    
        thrd_create(&t1,Counter,NULL);
        thrd_create(&t2,Counter,NULL);
    
        thrd_join(t1,NULL);
        thrd_join(t2,NULL);
        PRINT_INT(count);
        mtx_destroy(&mutex);
        return 0;
    }
    

    解决方法五 线程存储期

    #include <stdio.h>
    #include <tinycthread.h>
    #include <io_utils.h>
    
    _Thread_local int count = 0;//每个线程都有一个独立的副本
    int Counter(int* arg)
    {
        for(int i = 0;i<100000;i++)
        {
            count+=*arg;
            /*
             * int temp = count;
             * count=temp+1;
             * return temp;
             * */
        }
        PRINT_INT(count);
        return 0;
    }
    
    int main()
    {
        thrd_t t1;
        thrd_t t2;
        int arg_1 = 1;
        int arg_2 = 2;
        thrd_create(&t1,Counter,&arg_1);
        thrd_create(&t2,Counter,&arg_2);
    
        thrd_join(t1,NULL);
        thrd_join(t2,NULL);
        PRINT_INT(count);
    
        return 0;
        //count: 100000
        //count: 200000
        //count: 0
    }
    

    解决方法六 tss

    #include <stdio.h>
    #include <tinycthread.h>
    #include <io_utils.h>
    
    tss_t count_key;
    void MyFree(void*ptr)
    {
        PRINTLNF("free %#x",ptr);
        free(ptr);
    }
    int Counter(int* arg)
    {
        int* count = malloc(sizeof(int));
        *count = 0;
        if(tss_set(count_key,count) == thrd_success) //如果绑定成功
        {
            for (int i = 0; i < 100000; i++) {
                *count += *arg;
                /*
                 * int temp = count;
                 * count=temp+1;
                 * return temp;
                 * */
            }
        }
        PRINT_INT(*count);
        PRINT_INT(*((int*)tss_get(count_key)));
        return 0;
    }
    
    int main()
    {
        if(tss_create(&count_key,MyFree)==thrd_success)
        {
            thrd_t t1;
            thrd_t t2;
    
            int arg_1 = 1;
            int arg_2 = 2;
    
            thrd_create(&t1,Counter,&arg_1);
            thrd_create(&t2,Counter,&arg_2);
            //tss_delete(count_key); 如果在线程结束前删除,则不会调用MyFree,需要自己手动释放内存。
            thrd_join(t1,NULL);
            thrd_join(t2,NULL);
    
            puts("t_1,t_2 ends");
            tss_delete(count_key);
            PRINTLNF("count_key delete");
        }
        return 0;
    }
    
  • 相关阅读:
    MD53D模型
    WPF 关于变换
    WPF 关于圆角的制作
    [转载]MVVM、MVVMLight、MVVMLight Toolkit之我见
    Surface Pro 调整空间大小
    WPF 图片浏览 伪3D效果
    WPF 实现视频循环播放
    WPF MediElement 视频播放
    WPF INotifyPropertyChanged
    Matlab摄像头视频基本处理
  • 原文地址:https://www.cnblogs.com/chengmf/p/14946042.html
Copyright © 2020-2023  润新知