线程的同步与互斥

线程同步与互斥:

我们先来一个示例看一下:

#include <stdio.h>  
#include <stdlib.h>
#include <pthread.h>
#include <string.h>
#include <unistd.h>
#include <sys/syscall.h>

int i = 10;

void* fun(void* arg)
{
    char* id = (char *)arg;
    while(1)
    {
        if(i > 0)
        {
            sleep(1);
            printf("%s say i=%d\n",id,i);
            i--;
        }
        else
        {
            break;
        }
    }
    return NULL;
}

int main()
{
    int i = 0;
    pthread_t tid1,tid2,tid3,tid4;
    pthread_create(&tid1,NULL,fun,"1");
    pthread_create(&tid2,NULL,fun,"2");
    pthread_create(&tid3,NULL,fun,"3");
    pthread_create(&tid4,NULL,fun,"4");

    pthread_join(tid1,NULL);
    pthread_join(tid2,NULL);
    pthread_join(tid3,NULL);
    pthread_join(tid4,NULL);
    return 0;
}

按照我们理解的这个函数应该会输出:i从10—1的顺序。那么结果呢?

这里写图片描述

怎么会出现呢-1,-2呢?

这里写图片描述

这就是这个程序会出错的表面原因。那根本原因是什么呢?

这里就涉及到了原子性的概念,我们来看一下在cpu的角度下对待–操作是什么样的?

扩展一个指令,查看当前程序的汇编代码:

[test@localhost lianxi]$ objdump -d test > test.objdump
  400724:   8b 05 22 09 20 00       mov    0x200922(%rip),%eax        # 60104c <i>
  40072a:   83 e8 01                sub    $0x1,%eax
  40072d:   89 05 19 09 20 00       mov    %eax,0x200919(%rip)        # 60104c <i>

这是函数执行的汇编代码中“ - - ”操作。我们可以看到–操作在cpu中执行了3步,第一步将i的值移动到寄存器中,第二步cpu进行-1操作,第三步将操作完成的值写回到i的地址处。

这个中间的过程中如果进行了线程切换,那将会产生错误。

归根结底还是–操作不是原子性操作。

想解决这个问题,很简单就是创建一个锁。每次对一个值进行操作的前提必须拥有锁。而我们知道线程中是有共享的内存的,所以我们就在共享内存的地方创建一个锁,使每个线程都可以看到这把锁。Linux中称这把锁为“互斥量”。

互斥量:

一、互斥锁
互斥锁,是一种信号量,常用来防止两个进程或线程在同一时刻访问相同的共享资源。
需要的头文件:pthread.h
互斥锁标识符:pthread_mutex_t

(1)互斥锁初始化:
函数原型: int pthread_mutex_init (pthread_mutex_t* mutex,const pthread_mutexattr_t* mutexattr);
函数传入值:  mutex:互斥锁。
mutexattr:PTHREAD_MUTEX_INITIALIZER 创建快速互斥锁。
PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP 创建递归互斥锁。
PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP  创建检错互斥锁。
函数返回值:成功:0;出错:-12)互斥操作函数
int pthread_mutex_lock(pthread_mutex_t* mutex); //上锁
int pthread_mutex_trylock (pthread_mutex_t* mutex); //只有在互斥被锁住的情况下才阻塞
int pthread_mutex_unlock (pthread_mutex_t* mutex); //解锁
int pthread_mutex_destroy (pthread_mutex_t* mutex); //清除互斥锁
函数传入值:mutex:互斥锁。
函数返回值:成功:0;出错:-1

我们改进一下之前的代码:

int i = 10;
pthread_mutex_t mutex;

void* fun(void* arg)
{
    char* id = (char *)arg;
    while(1)
    {
        pthread_mutex_lock(&mutex);
        if(i > 0)
        {
            printf("%s say i=%d\n",id,i);
            i--;
            pthread_mutex_unlock(&mutex);
        }
        else
        {
            pthread_mutex_unlock(&mutex);
            break;
        }
        sleep(1);
    }
    return NULL;
}

int main()
{
    int i = 0;
    pthread_t tid1,tid2,tid3,tid4;
    pthread_create(&tid1,NULL,fun,"1");
    pthread_create(&tid2,NULL,fun,"2");
    pthread_create(&tid3,NULL,fun,"3");
    pthread_create(&tid4,NULL,fun,"4");

    pthread_join(tid1,NULL);
    pthread_join(tid2,NULL);
    pthread_join(tid3,NULL);
    pthread_join(tid4,NULL);
    return 0;
}

效果如下:
这里写图片描述

这样效果就出来了吧!

线程同步:(条件变量)

  • 当线程访问互斥的某个变量时,它在其他线程没有释放“锁”之前什么也干不了。
  • 例如动物园里饲养员给动物为食,在饲养员正在放食物的时候,动物被关在笼子了,什么也干不了。只有当饲养员把食物放好后,打开笼子,动物才能吃东西。

有关函数:

int pthread_cond_destroy(pthread_cond_t *cond);
//删除条件变量
int pthread_cond_init(pthread_cond_t *restrict cond,
              const pthread_condattr_t *restrict attr);
              //初始化
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
//创建
int pthread_cond_wait(pthread_cond_t *restrict cond,
              pthread_mutex_t *restrict mutex);
//等待条件满足(动物在笼子等待饲养员喂食物的过程)
int pthread_cond_broadcast(pthread_cond_t *cond);
//唤醒多个线程(饲养员放好了食物,打开了许多动物的笼子)
int pthread_cond_signal(pthread_cond_t *cond);
//唤醒单个线程(饲养员放好了食物,只打开了一个动物的笼子)

示例:

pthread_cond_t cond;
pthread_mutex_t mutex;

void* fun()
{
    while(1)
    {
        pthread_cond_wait(&cond,&mutex);
        printf("tager\n");
    }
}

void* fun2()
{
    while(1)
    {
        pthread_cond_signal(&cond);
        sleep(1);
    }
}

int main()
{
    int i = 0;

    pthread_cond_init(&cond,NULL);
    pthread_mutex_init(&mutex,NULL);

    pthread_t tid1,tid2;

    pthread_create(&tid1,NULL,fun,NULL);
    pthread_create(&tid2,NULL,fun2,NULL);

    pthread_join(tid1,NULL);
    pthread_join(tid2,NULL);

    pthread_cond_destroy(&cond);
    return 0;
}

效果实例:
这里写图片描述

在这个pthread_cond_wait函数中为什么第二个参数要是一个互斥锁呢?

条件等待是线程通信间的一种必要的方式,如果一个线程所需要的资源不满足,他就会一直等待,所以必须要有另一个线程来改变它,这时就是俩个线程共享一块资源了,所以必须要加上锁。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值