线程同步

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

 

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

 

如我们有一个共享内存数据资源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\n", 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.\n", 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\n");
            exit(-1);
        }
    }
    
    /*主线程退出*/
    pthread_exit(NULL);
}

 

运行结果: 

 

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

posted @ 2019-12-04 13:59  王清河  阅读(279)  评论(0编辑  收藏  举报