linux线程3-互斥与同步

简介

  • 多线程共享一个进程的地址空间,多线程间通信容易进行,但是多线程同时访问共享对象时需要引入同步和互斥机制.
  • 同一个进程的线程共享进程内的绝大部分资源,当一段访问这些共享资源的代码块,有可能被多个线程执行,那么这块代码就被叫做临界区.
  • 当有多个线程并发的在临界区执行时,程序的执行结果会出现不确定性,这种情况被叫做静态条件.

一个demo代码

//
// Created by wuxiao on 17-5-29.
//

#include <stdlib.h>
#include <stdio.h>
#include <pthread.h>
#include <errno.h>

#ifndef handle_error_en
#define handle_error_en(en,msg)\
    errno = en;perror(msg);exit(EXIT_FAILURE);
#endif
//变量名等规则这里先不考虑
//多线程共享资源
static int global = 0;

static void *thread_routine(void *arg)
{
    int loc,j;
    for(j = 0; j < 10000000;j++)
    {
        loc = global;
        loc++;
        global = loc;
    }
    return nullptr;
}

int main(int argc,char* argv[])
{
    pthread_t t1,t2;
    int result;

    //创建两个线程,并修改共享资源global
    result = pthread_create(&t1,NULL,thread_routine,NULL);
    result += pthread_create(&t2,NULL,thread_routine,NULL);
    if(result != 0)
    {
        handle_error_en(result,"pthread_create");
    }

    //等待子线程退出
    result = pthread_join(t1, nullptr);
    result += pthread_join(t2, nullptr);
    if(result != 0)
    {
        handle_error_en(result,"pthread_join");
    }
    printf("global = %d\n",global);
    exit(EXIT_SUCCESS);
}

这里写图片描述
- 发现共享变量不是预期值,原因就是进入了静态条件,需要保证多个线程在临界区时互斥的,常用方法是加互斥锁
- 锁分静态和动态:

/ 静态初始化一个全局的互斥锁
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);
  • 加锁后的demo代码
//
// Created by wuxiao on 17-5-29.
//

#include <stdlib.h>
#include <stdio.h>
#include <pthread.h>
#include <errno.h>

#ifndef handle_error_en
#define handle_error_en(en,msg)\
    errno = en;perror(msg);exit(EXIT_FAILURE);
#endif
//变量名等规则这里先不考虑
//多线程共享资源
static int global = 0;
//静态锁
static pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER;

static void *thread_routine(void *arg)
{
    int loc,j;
    for(j = 0; j < 10000000;j++)
    {
        //加锁
        pthread_mutex_lock(&mtx);
        loc = global;
        loc++;
        global = loc;
        //解锁
        pthread_mutex_unlock(&mtx);
    }
    return nullptr;
}

int main(int argc,char* argv[])
{
    pthread_t t1,t2;
    int result;

    //创建两个线程,并修改共享资源global
    result = pthread_create(&t1,NULL,thread_routine,NULL);
    result += pthread_create(&t2,NULL,thread_routine,NULL);
    if(result != 0)
    {
        handle_error_en(result,"pthread_create");
    }

    //等待子线程退出
    result = pthread_join(t1, nullptr);
    result += pthread_join(t2, nullptr);
    if(result != 0)
    {
        handle_error_en(result,"pthread_join");
    }
    printf("global = %d\n",global);
    exit(EXIT_SUCCESS);
}

这里写图片描述

线程同步

  • 加解锁一般是为了解决竞态条件的问题,但是有时候我们需要让线程按序执行,这种确保多线程间按照先后顺序执行的技术,就叫做线程同步.
  • 条件变量是线程同步的主要手段,例如线程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);
  • demo代码:
//
// Created by wuxiao on 17-5-29.
//

#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;
//全局写入次数变量
#define MAX_WRITE_TIMES 10
int  write_times = 0;

struct file_res
{
    //当前文件写入的线程
    pthread_t *writer;
    //文件描述符
    int fd;
}file_res =
        {
                .writer = &threads[0],
        };

//两个线程的入口函数
void* writer_routine(void *arg)
{
    int index = (intptr_t)arg;
    size_t i = 0;
    int next_index = 0;
    printf("thread %d is running,will write %c\n",index,writer_char[index]);


    while(1)
    {
        //如果已经到达最大写入次数
        if(write_times >= MAX_WRITE_TIMES)
        {
            //结束当前线程
            pthread_exit(NULL);
        }
        if(pthread_mutex_lock(&mutex) != 0)
        {
            exit(EXIT_FAILURE);
        }

        for(;;)
        {
            //如果当前线程可写入文件,写文件
            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];
                write_times++;
                //跳出死循环,通知下一个线程进场
                break;
            }
            //当前线程不可写,等待下一个线程唤醒
            pthread_cond_wait(&cond,&mutex);

        }


        //解锁并唤醒下一个线程
        if(pthread_mutex_unlock(&mutex) != 0)
        {
            exit(EXIT_FAILURE);
        }
        pthread_cond_signal(&cond);
    }



}

int main(int argc,char* argv[])
{
    //创建文件
    char file_name[] = "file";
    if(file_res.fd = open(file_name,O_RDWR|O_CREAT|O_TRUNC,0666) < 0)
    {
        printf("open %s fail\n",file_name);
        exit(EXIT_FAILURE);
    }

    //创建线程1和2
    size_t i;
    for(i = 0;i < sizeof(threads)/sizeof(pthread_t);i++)
    {
        if(pthread_create(&threads[i],NULL,writer_routine,(void*)(intptr_t)i) != 0)
        {
            printf("create thread %zu fail\n", i);
            exit(EXIT_FAILURE);
        }
    }

    pthread_exit(NULL);

}

这里写图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值