linux之线程互斥(万字长文详解)

时间:2024-01-21 19:30:56

我们说过为了解决多线程访问共享资源的问题于是就有了一个解决方案——加锁

那么锁是什么呢?

image-20230815113224253

我们可以看到是一个pthread_mutex_t的数据类型——==这就是锁==

image-20230815165955904

就是一个联合体

有一个专门用来初始化这个锁的函数pthread_mutex_init

第一个参数就是一个输出型参数——将我们定义的锁传进去,进行初始化!

还有一个专门释放这个锁的函数pthread_mutex_destroy

该函数只有一个参数!那就是要释放的锁的地址!

==有了所我们就要用这个锁!——那么就要加锁和解锁!==

image-20230815164843523

我们要使用的使用的时候就用pthread_mutex_lock进行加锁!

我们不想使用的试试就用pthread_mutex_unlock进行解锁!

==这样子我们就可以通过对共享资源进行加锁实现共享资源临界资源化!==

int tickets = 1000;//充当有1000张票
class ThreadData
{
public:
    ThreadData(const std::string &threadname,pthread_mutex_t* mutex_p)
        : threadname_(threadname),
        mutex_p_(mutex_p)
    {}

public:
    std::string threadname_;
    pthread_mutex_t* mutex_p_;
};
void* getTicket(void* args)
{
    ThreadData* td = static_cast<ThreadData*>(args);
    while(true)
    {
         pthread_mutex_lock(td->mutex_p_);
        if(tickets>0)
        {
            usleep(1245);
            std::cout << td->threadname_ << " 正在抢票" << tickets<<std::endl;
            tickets--;
            pthread_mutex_unlock(td->mutex_p_);
        }
        else {
            pthread_mutex_unlock(td->mutex_p_);
            //满足了它会加锁后解锁!
            //不满足也要加锁后解锁!
            //如果不解锁后果很严重
            break;
        }
    }
    return nullptr;
}
int main()
{
    pthread_mutex_t lock;//只要是一把公共的锁!
    //那么我们就可以使用这个公共的锁对临界资源进行加锁!
    pthread_mutex_init(&lock,nullptr); //初始化这个锁
#define NUM 4
    std::vector<pthread_t> tids(NUM);
    for (int i = 0; i < NUM; i++)
    {
        char buffer[64];
        snprintf(buffer,sizeof buffer,"thread %d",i);
        ThreadData *td = new ThreadData(buffer,&lock);
        pthread_create(&tids[i],nullptr,getTicket,td);
    }
    for(const auto &tid:tids)
    {
        pthread_join(tid,nullptr);
    }

    pthread_mutex_destroy(&lock);//不使用后销毁
}

image-20230815172523821

这样子我们可以看到就不会出现出现数据出现负数的问题了!——但是为什么只有一个线程在抢?而且运行速度变慢了!

==加锁和解锁的过程是多个线程串行执行的!所以这就是为什么程序变慢了!==

==那为什么只有一个线程一直在运行呢?——因为当第一个线程抢到锁之后,其他线程就进入休眠!等到第一个线程执行完毕解锁之后!然后在解锁后,第一个线程又立马抢到锁了!其他线程没有抢到!那么就只能每一次都让第一个线程去运行!==

互斥锁只规定互斥访问!没有规定必须让谁去优先执行!——只要谁的竞争能力强!那么谁就能一直持有锁!

所以锁就是让多个执行流竞争的结果!——因为第一个线程竞争锁的能力更强,所以他就能一直持有锁!

void* getTicket(void* args)
{
    // std::string username =  static_cast<const char*>(args);
    ThreadData* td = static_cast<ThreadData*>(args);
    while(true)
    {

        pthread_mutex_lock(td->mutex_p_);
        if(tickets>0)
        {
            usleep(1245);
            std::cout << td->threadname_ << " 正在抢票" << tickets<<std::endl;
            tickets--;
            pthread_mutex_unlock(td->mutex_p_);
        }
        else {
            pthread_mutex_unlock(td->mutex_p_);
            break;
        }
        // 实际中抢票也不只是--,就完毕了!还有其他事情要做!
        // 这是只有在这段时间里面!其他线程才有可能拿到锁!
        // 所以我们用usleep来模拟,抢完票后处理其他事情的时间
        usleep(1231); // 例如:形成订单给用户
    }
    return nullptr;
}

image-20230815173906402

==除了上面的那种加锁方式之外!我们还有其他的加锁方式——例如将锁定义为静态的,或者全局的!==

一旦这把锁被定义为了全局的!那么我们就不用使用数pthread_mutex_init进行初始化和pthread_mutex_destroy进行销毁

pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;//只要这样初始化

==然后就可以直接使用了!==

#include<iostream>
#include<pthread.h>
#include<cstdio>
#include<string>
#include<cstring>
#include<unistd.h>
pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;//全局变量
int tickets = 1000;
class ThreadData
{
    public:
       ThreadData(const std::string &threadname,pthread_mutex_t* mutex_p)
           : threadname_(threadname),
       mutex_p_(mutex_p)
       {}

    public:
       std::string threadname_;
       pthread_mutex_t* mutex_p_;
};
void* getTicket(void* args)
{
       std::string username =  static_cast<const char*>(args);
       while(true)
       {

           pthread_mutex_lock(&lock);//直接进行加锁和解锁
           if(tickets>0)
           {
               usleep(1245);
               std::cout << username << " 正在抢票" << tickets<<std::endl;
               tickets--;
               pthread_mutex_unlock(&lock);
           }
           else {
               pthread_mutex_unlock(&lock);
               break;
           }
           usleep(1231);
       }
       return nullptr;
}
int main()
{
       pthread_t t1,t2,t3,t4;
       pthread_create(&t1,nullptr,getTicket,(void*)"thread 1");
       pthread_create(&t2,nullptr,getTicket,(void*)"thread 2");
       pthread_create(&t3,nullptr,getTicket,(void*)"thread 3");
       pthread_create(&t4,nullptr,getTicket,(void*)"thread 4");

       pthread_join(t1,nullptr);
       pthread_join(t2,nullptr);
       pthread_join(t3,nullptr);
       pthread_join(t4,nullptr);

}