• 线程同步


    我们使用互斥锁解决了多个线程的竞态条件问题。

    互斥锁的一个主要特点是,谁先拿到锁先就可以优先访问共享资源,因此多个线程访问共享资源的互斥性是得到了保证,但是在某些场合可能还希望确保线程间执行的顺序。

    如我们有一个共享内存数据资源M,我们整个程序设计需求是要求线程A在M上做了处理之后,线程B才能做处理。这种需要确保多线程间执行先后顺序的技术,称为线程的同步。

    条件变量是线程同步的主要手段。其大致的实现思想就是:

    线程B,调用条件变量的接口让自身阻塞;

    线程A,在处理完资源后,通过条件变量接口唤醒正在等待该资源的线程B。

    条件变量的初始化也有静态初始化和动态初始化两种方式。

    //静态初始化
    // 与互斥锁类似静态初始化一个全局的条件变量
    pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
    
    //动态初始化
    #include <pthread.h>
    int pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *attr);
    
    //通知和等待条件变量
    #include <pthread.h>
    // 等待一个指定的条件变量
    int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);
    // 唤醒一个等待该条件变量的线程
    int pthread_cond_signal(pthread_cond_t *cond);
    // 唤醒所有等待该条件变量的线程
    int pthread_cond_broadcast(pthread_cond_t *cond);

    需要使用条件变量时,总是意味着有多个线程在使用某一共享资源或状态,而条件变量本身并不提供互斥性的保证,因此,条件变量需要结合互斥锁使用。 我们用伪代码来演示,如何使用条件变量解决上述线程A和线程B的同步问题:

    //全局初始化
    pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
    pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER;
    
    //线程A
      pthread_mutex_lock(&mtx);
      // 处理资源M
      // 处理完资源M后,设置资源M的状态为线程B可用
      pthread_mutex_unlock(&mtx);
    
      pthread_cond_signal(&cond);
    
    //线程B
      pthread_mutex_lock(&mtx);
      while(/*资源M线程B不可用*/)
          pthread_cond_wait(&cond, &mtx);
      // 开始处理资源M
      pthread_mutex_unlock(&mtx);

    线程B的pthread_cond_wait函数需要一个互斥锁参数,其内部会执行以下操作:

    解锁互斥锁

    阻塞调用线程,直到等待的条件变量被唤醒

    重新锁定互斥锁

    下面我们通过通过来实例演示如何使用条件变量来同步线程。

    题目如下:

    有两个线程线程1和线程2:线程1的功能就是输出A,线程2的功能就是输出B。

    现在有一个文件file初始为空。要让该文件呈如下格式:

    file:ABAB....

    /*
    pthread_cond_sample.c
    */
    #include <stdio.h>
    #include <pthread.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    #include <unistd.h>
    #include <stdlib.h>
    #include <stdint.h>
    
    
    pthread_t threads[2];
    char writer_char[2] = {'A', 'B'};
    pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
    pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
    
    
    struct file_res{
        pthread_t *writer; /*当前文件可以被哪个线程写入*/
        int fd; /*文件描述符*/
    }file_res =
    {
        .writer=&threads[0],/*file初始化可以被线程1写入'A'*/
    };
    
    /*线程1和线程2的入口函数*/
    void *writer_routine(void *arg)
    {
        int index = (intptr_t)arg;
        int i = 0;
        int next_index=0;
        printf("thread %d is running, and will write '%c' to file
    ", index, writer_char[index]);
        
        while(1)
        {
            if (0!=pthread_mutex_lock(&mutex))
                exit(-1);
            for(;;) {
                
                /*如果当前线程可写file, 执行写操作*/
                if (&threads[index]==file_res.writer) {
                    write(file_res.fd, &writer_char[index],         
                          sizeof(writer_char[index]));
                    
                    /*更新下一个可写线程*/      
                    next_index = (index+1)%2;
                    file_res.writer = &threads[next_index];
                    
                    /*执行写操作后,break for循环通过条件变量通知其他线程写*/
                    break;
                }
                
                /*当前线程不可写,等待其他线程唤醒*/
                pthread_cond_wait(&cond,&mutex);
            }        
            
            if (0!=pthread_mutex_unlock(&mutex))
                exit(-1); 
                
            /*唤醒下一个线程*/
            pthread_cond_signal(&cond);
       
        }
    }
     
    int main(int argc, char* argv[])
    {
    
        /*创建空文件file*/
        char file_name[] = "file";
        if ((file_res.fd = open(file_name, O_RDWR|O_CREAT|O_TRUNC, 0666)) < 0)
        {
            printf("open %s error.
    ", file_name);
            exit(-1);
        }
        
        /*创建线程1和线程2*/
        int i;
        for (i=0; i<(sizeof(threads)/sizeof(pthread_t)); i++)
        {
            if(pthread_create(&threads[i], NULL, writer_routine, (void *)(intptr_t)i))
            {
                printf("create writer thread error
    ");
                exit(-1);
            }
        }
        
        /*主线程退出*/
        pthread_exit(NULL);
    }

     

    运行结果: 

     

    打开同目录下的file文件都是很多个AB互写。

  • 相关阅读:
    没想到吧?这货比 open 更适合读取文件
    卸载 PyCharm!这才是 Python 小白的最理想的 IDE
    git 会保留所有的提交吗?不会!
    C# 在构造函数内调用虚方法
    【转】第一个汇编器是怎么实现的
    SQL Server查询数据库所有表名与表说明
    Vue实现节流,防止重复提交
    mysql 查询json数组(一)
    VScode怎么在代码折叠后,插入新的内容
    Vue 通过调用百度API获取地理位置-经度纬度省份城市
  • 原文地址:https://www.cnblogs.com/wanghao-boke/p/11982512.html
Copyright © 2020-2023  润新知