• 线程互斥


    竞态条件

    同一个进程的线程共享进程内的绝大部分资源,当一段访问这些共享资源的代码块,有可能被多个线程执行时,那么这段代码块就称为临界区。 当有多个线程并发的在临界区执行时,程序的执行结果会出现不确定性,这种情况称之为竞态条件。

    实例:

    #include<stdio.h>
    #include<pthread.h>
    #include<stdlib.h>
    #include<errno.h>
    
    #define handle_error_en(en, msg) 
        do { errno = en; perror(msg); exit(EXIT_FAILURE); } while(0)
    
    //glob为多线程共享资源
    static int glob = 0;
    
    //子线程访问修改共享资源glob
    static void *thread_routine(void *arg)
    {
        int loc, j;
        for (j = 0; j < 10000000; j++)
        {
            loc = glob;
            loc++;
            glob = loc;
        }
        return NULL;
    }
    
    int main()
    {
        pthread_t t1,t2;
        int s;
    
        //创建两个线程并发访问修改共享资源glob
        s = pthread_create(&t1, NULL, thread_routine, NULL);
        if (0 != s)
        {
            handle_error_en(s, "pthread_create");
        }
    
        s = pthread_create(&t2, NULL, thread_routine, NULL);
        if (0 != s)
        {
            handle_error_en(s, "pthread_create");
        }
    
        s = pthread_join(t1, NULL);
        if (0 != s)
        {
            handle_error_en(s, "pthread_join");
        }
        s = pthread_join(t2, NULL);
        if (0 != s)
        {
            handle_error_en(s, "pthread_join");
        }
    
        printf("glob = %d
    ", glob);
        exit(EXIT_SUCCESS);
    }

    运行结果:

    上述代码,每个线程都对glob进行了10000000次加1操作,glob的初值为0,因此理论上程序的执行结果应该是20000000。但是实际结果却没有达到预期。

    出现以上结果的原因是因为多个线程并发的访问该临界区,从而出现了竞态条件。两个线程有可能以以下的时序执行,从而导致glob的最终值小于20000000。

    互斥锁


    多线程编程中,避免出现竞态条件的一项重要解决方案就是,保证多个线程在临界区是互斥的。所谓的互斥,就是指不能同时有多于一个线程进入临界区。 保证临界区互斥的重要技术,就是互斥锁。 互斥锁的初始化,有两种方式:静态初始化和动态初始化。

    // 静态初始化一个全局的互斥锁
    pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER;
    #include <pthread.h>
    // 动态分配一个互斥锁
    int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr);
    // 释放动态分配的互斥锁
    int pthread_mutex_destroy(pthread_mutex_t *mutex);
    #include <pthread.h>
    // 持有互斥锁
    int pthread_mutex_lock(pthread_mutex_t *mutex);
    // 释放互斥锁
    int pthread_mutex_unlock(pthread_mutex_t *mutex);

    使用互斥锁,保证临界区互斥的一般思路是:

      1. 为该临界区分配一把互斥锁;
      2. 任何想要进入临界区的线程都必须先持有该互斥锁;
      3. 持有互斥锁运行于临界区的线程在离开临界区后必须释放该互斥锁;
      4. 假设某一临界区正在被一个线程A执行着,这意味着线程A持有该临界区的互斥锁M,如果此时有另一个线程B企图持有互斥锁M进入临界区,那么线程B将会进入阻塞状态。

    使用互斥锁最常见的错误就是死锁,而所谓的死锁是指一个线程为了持有一把互斥锁而永远的阻塞了。 造成死锁原因主要有以下两种:

    一个线程试图对其已经持有的互斥锁进行再次加锁;

    当有需要持有多把锁时,线程间加锁的顺序不同时,也会造成死锁,如下图所示:

    使用互斥锁修正glob的计算问题源代码如下:

    #include<stdio.h>
    #include<pthread.h>
    #include<stdlib.h>
    #include<errno.h>
    
    #define handle_error_en(en, msg) 
        do { errno = en; perror(msg); exit(EXIT_FAILURE); } while(0)
    
    //glob为多线程共享资源
    static int glob = 0;
    
    static pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER;
    
    //子线程访问修改共享资源glob
    static void *thread_routine(void *arg)
    {
        int loc, j;
        for (j = 0; j < 10000000; j++)
        {
            pthread_mutex_lock(&mtx);
            loc = glob;
            loc++;
            glob = loc;
            pthread_mutex_unlock(&mtx);
        }
        return NULL;
    }
    
    int main()
    {
        pthread_t t1,t2;
        int s;
    
        //创建两个线程并发访问修改共享资源glob
        s = pthread_create(&t1, NULL, thread_routine, NULL);
        if (0 != s)
        {
            handle_error_en(s, "pthread_create");
        }
    
        s = pthread_create(&t2, NULL, thread_routine, NULL);
        if (0 != s)
        {
            handle_error_en(s, "pthread_create");
        }
    
        s = pthread_join(t1, NULL);
        if (0 != s)
        {
            handle_error_en(s, "pthread_join");
        }
        s = pthread_join(t2, NULL);
        if (0 != s)
        {
            handle_error_en(s, "pthread_join");
        }
    
        printf("glob = %d
    ", glob);
        exit(EXIT_SUCCESS);
    }

    运行结果:

  • 相关阅读:
    vant的table
    element table行hover时显示弹窗
    [转]Java基础面试题(2022最新版汇总)
    Windows开机自启动运行Java的jar包
    CEPH的CephFS和RDB的区别
    readlink
    使用系统日志定位coredump
    MySql是否需要commit详解
    mysql预编译
    trap命令
  • 原文地址:https://www.cnblogs.com/wanghao-boke/p/11976437.html
Copyright © 2020-2023  润新知