【线程同步与互斥】卖票问题(互斥锁)

一、简述

1.共享变量
很多变量有时候需要在线程间共享,可以通过数据的共享,从而完成线程之间的交互。如果变量是只读的,多个线程同时读取该变量不会有一致性的问题,但是当一个线程可以修改的变量,我们就需要对这些线程进行同步了。
2.互斥
指某一资源同时只允许一个线程访问,具有唯一性和排他性。但互斥无法限制访问者对资源的访问顺序,即访问是无序的,有可能造成某些线程的饥饿问题,长时间享受不到资源。线程互斥可以看成是一种特殊的线程同步。
3.同步
同步也是不能同时运行,但它必须是按照某种次序来运行相应的线程。
这里写图片描述

二、售票系统(错误操作共享变量代码)

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
int ticket=100;
void *route(void *arg)
{
    char *id=(char*)arg;
    while(1)
    {
        if(ticket>0)
        {
            usleep(100000);
            printf("%s sells ticket:%d\n",id,ticket);
            ticket--;
        }
        else
        {
            break;
        }
    }
}
int main()
{
    pthread_t t1,t2,t3,t4;
    pthread_create(&t1,NULL,route,"thread 1");
    pthread_create(&t2,NULL,route,"thread 2");
    pthread_create(&t3,NULL,route,"thread 3");
    pthread_create(&t4,NULL,route,"thread 4");
    pthread_join(t1,NULL);
    pthread_join(t2,NULL);
    pthread_join(t3,NULL);
    pthread_join(t4,NULL);
    return 0;
}

结果图:
这里写图片描述
包含两个问题,进入的顺序不同,最后票数变成了负数。在线程操作函数中,if语句成立后,代码可以并发的切换到其他进程,可能有很多个线程同时进入该代码段,ticket–本身就不是一个原子操作。在汇编代码ticket–中,可以发现,其对应的是三条汇编代码。

//汇编干了什么?
将共享变量ticket从内存加载到寄存器中
更新寄存器里面的值,执行减一操作
将新值,从寄存器写回共享变量的内存地址。
//查看汇编代码
objdump -d a.out > test.objdump

所以要想解决这个问题,就需要一把锁,在linux中,这把锁叫互斥锁,也叫互斥量,这把锁也算是一个临界资源。

三、改进售票系统(使用互斥量)

1.互斥量接口
1)初始化互斥量

//静态分配
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER
//动态分配
int pthread_mutex_init(pthread_mutex_t *restrict mutex,\
const pthread_mutexattr_t *restrict attr);
//mutex是要初始化的互斥量,attr:NULL

2)销毁互斥量

int pthread_mutex_destroy(pthread_mutex_t *mutex);
//使用静态初始化的互斥量不需要销毁,不要销毁一个已经加锁的互斥量

3)加锁与解锁

int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_unlock(pthread_mutex_t *mutex);
//成功返回零,失败返回错误号
//如果多个线程同时申请互斥量,但没有竞争到互斥量,pthread_mutex_unlock调用会陷入阻塞,等待互斥量解锁。

2.改进代码

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>

int ticket=100;
pthread_mutex_t mutex;

void *route(void *arg)
{
    char *id=(char*)arg;
    while(1)
    {
        pthread_mutex_lock(&mutex);
        if(ticket>0)
        {
            usleep(100000);
            printf("%s sells ticket:%d\n",id,ticket);
            ticket--;
            pthread_mutex_unlock(&mutex);
        }
        else
        {
            pthread_mutex_unlock(&mutex);
            break;
        }
    }
}

int main()
{
    pthread_mutex_init(&mutex,NULL);
    pthread_t t1,t2,t3,t4;
    pthread_create(&t1,NULL,route,"thread 1");
    pthread_create(&t2,NULL,route,"thread 2");
    pthread_create(&t3,NULL,route,"thread 3");
    pthread_create(&t4,NULL,route,"thread 4");

    pthread_join(t1,NULL);
    pthread_join(t2,NULL);
    pthread_join(t3,NULL);
    pthread_join(t4,NULL);
    pthread_mutex_destroy(&mutex);
    return 0;
}

这里写图片描述
注:虽然互斥量解决了卖票出错问题,也就是说不会出现负数的票,但也导致了新的问题,互斥量只有锁定和非锁定两种状态,这就有可能导致其他线程的饥饿问题,因为它们并不知道何时应该使用共享资源。

猜你喜欢

转载自blog.csdn.net/guorong520/article/details/80246381