【Linux】多线程深度剖析(二)

本文深入探讨Linux多线程,包括死锁的概念及避免策略,生产者消费者模型的原理与应用,以及POSIX信号量在多线程同步中的使用。同时,文章还介绍了线程池的概念、应用场景和示例,以及STL容器、智能指针的线程安全性问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1. 死锁

1.1 死锁的概念

死锁是指在一组进程中的各个进程均占有不会释放的资源,但因互相申请被其他进程所站用不会释放的资源而处于的一种永久等待状态。

1.2 死锁四个必要条件

  • 互斥条件:一个资源每次只能被一个执行流使用。
  • 请求与保持条件:一个执行流因请求资源而阻塞时,对已获得的资源保持不放。
  • 不剥夺条件:一个执行流已获得的资源,在末使用完之前,不能强行剥夺。
  • 循环等待条件:若干执行流之间形成一种头尾相接的循环等待资源的关系。

1.3 避免死锁

  • 破坏死锁的四个必要条件
  • 加锁顺序一致
  • 避免锁未释放的场景
  • 资源一次性分配

1.4 简单案例

// thread_safe_deadlock.cpp
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>

#define THREAD_COUNT 2

pthread_mutex_t tex1;
pthread_mutex_t tex2;

int g_tickets = 100;

struct ThreadData
{
   
    int num_;
    ThreadData()
    {
   
        num_ = -1;
    }
};

void* TreadStart1(void* arg)
{
   
    pthread_detach(pthread_self());
    pthread_mutex_lock(&tex1);

    sleep(5);
    pthread_mutex_lock(&tex2);
}

void* TreadStart2(void* arg)
{
   
    pthread_detach(pthread_self());
    pthread_mutex_lock(&tex2);

    sleep(5);
    pthread_mutex_lock(&tex1);
}

int main()
{
   
    pthread_mutex_init(&tex1, NULL);
    pthread_mutex_init(&tex2, NULL);
    pthread_t tid;

    int ret = pthread_create(&tid, NULL, TreadStart1, NULL);
    if (ret < 0)
    {
   
        perror("pthread_create");
        return -1;
    }

    ret = pthread_create(&tid, NULL, TreadStart2, NULL);
    if (ret < 0)                                            
    {
              
        perror("pthread_create");
        return -1;               
    }        

    while (1)
    {
   
        //printf("i am main thread\n");
        sleep(1);
    }

    pthread_mutex_destroy(&tex1);
    pthread_mutex_destroy(&tex2);
    return 0;
}

2. 生产者消费者模型

2.1 三二一原则

  • 一个线程安全队列;
  • 两种角色:生产者与消费者;
  • 三种关系。

2.2 为何要使用生产者消费者模型

生产者消费者模式就是通过一个容器来解决生产者和消费者的强耦合问题。
生产者和消费者彼此之间不直接通讯,而通过阻塞队列来进行通讯,所以生产者生产完数据之后不用等待消费者处理,直接扔给阻塞队列,消费者不找生产者要数据,而是直接从阻塞队列里取,阻塞队列就相当于一个缓冲区,平衡了生产者和消费者的处理能力。这个阻塞队列就是用来给生产者和消费者解耦的。

2.3 生产者消费者模型优点

  • 解耦:生产者和消费者都是通过队列进行交互的;
  • 支持并发
  • 支持忙闲不均
    在这里插入图片描述

2.4 基于BlockingQueue的生产者消费者模型

在多线程编程中**阻塞队列(Blocking Queue)**是一种常用于实现生产者和消费者模型的数据结构。
其与普通的队列区别在于,当队列为空时,从队列获取元素的操作将会被阻塞,直到队列中被放入了元素;当队列满时,往队列里存放元素的操作也会被阻塞,直到有元素被从队列中取出(以上的操作都是基于不同的线程来说的,线程在对阻塞队列进程操作时会被阻塞。

2.5 C++ queue模拟阻塞队列的生产消费模型

#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
#include <queue>

#define CAPACITY 5
#define THREAD_COUNT 2

pthread_mutex_t g_mutex;

//定义线程安全的队列
class SafeQueue
{
   
    public:
        SafeQueue()
        {
   
            capacity_ = CAPACITY;
            pthread_mutex_init(&mutex_, NULL);
            pthread_cond_init(&consume_cond_, NULL);
            pthread_cond_init(&product_cond_, NULL);
        }

        ~SafeQueue()
        {
   
            pthread_mutex_destroy(&mutex_);
            pthread_cond_destroy(&consume_cond_);
            pthread_cond_destroy(&product_cond_);
        }

        void Push(int data)
        {
   
            pthread_mutex_lock(&mutex_);
            while(queue_.size() >= capacity_)
            {
   
                pthread_cond_wait(&product_cond_, &mutex_); // 队列满,存放元素阻塞
            }
            queue_.push(data);
            pthread_mutex_unlock(&mutex_);
            pthread_cond_signal(&consume_cond_); // 通知消费者
        }

        void Pop(int* data)
        {
   
            pthread_mutex_lock(&mutex_);
            while(queue_.empty())
            {
   
                pthread_cond_wait(&consume_cond_, &mutex_); // 队列空,获取元素阻塞
            }
            *data = queue_.front();
            queue_.pop();
            pthread_mutex_unlock(&mutex_);
            pthread_cond_signal(&product_cond_); // 通知生产者
        }

    private:
        std::queue<int> queue_; //对queue_容量做一个限制
        size_t capacity_;

        pthread_mutex_t mutex_;
        pthread_cond_t consume_cond_;
        pthread_cond_t product_cond_;
};

void* consume_start(void* arg)
{
   
    SafeQueue* sq = (SafeQueue*) arg;
    while(1)
    {
   
        int data;
        sq->Pop(&data);
        printf("i am %p, i consume %d\n", pthread_self(), data);
    }
    return NULL;
}

int count 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值