从零开始学习Linux(13)---多线程

目录

1.线程

1.线程的概念

2.线程的理解(Linux系统为例)---一般系统

3.进程vs线程

4.线程的控制

5.线程的等待

6.线程的终止

7.线程的分离

2.线程的互斥

1.互斥锁

2.条件变量

3.生产消费模型

4.阻塞队列

5.信号量

6.唤醒队列


1.线程

1.线程的概念

线程是进程内部的一个执行分支,线程是CPU调度的基本单位。

加载到内存中的程序,叫做进程。修正=内核数据结构+进程代码和数据。

2.线程的理解(Linux系统为例)---一般系统

地址空间是计算机内存管理中的一个基本概念,它指的是一个程序在运行时可以访问的内存地址的集合。地址空间为程序提供了一个抽象层,使得程序可以独立于其他程序和物理内存的实际情况来访问内存。

  • 物理地址空间:指的是实际的物理内存地址,即内存条上的每一个存储单元都有一个唯一的物理地址。物理地址空间由硬件直接管理。

  • 虚拟地址空间:是相对于物理地址空间的一个抽象层,它由操作系统创建和管理。程序通常使用虚拟地址来访问内存,然后由操作系统和硬件将这些虚拟地址映射到物理地址。

虚拟地址到物理地址的映射通常通过页表来实现。当程序访问一个虚拟地址时,内存管理单元会查找页表,找到对应的物理地址,然后进行实际的内存访问。

地址空间和地址空间上的虚拟地址,本质是一种资源。

3.进程vs线程

进程是操作系统进行资源分配和调度的基本单位。每个进程都拥有独立的地址空间、执行堆栈、程序计数器、寄存器集合以及系统资源。

线程是进程内的一个执行流,是CPU调度和分派的基本单位。同一进程中的线程共享地址空间和其他资源。

4.线程的控制

pthread_create是 POSIX 线程(pthread)库中的一个函数,用于在 POSIX 兼容的操作系统中创建新的线程。以下是关于pthread_create 的详细信息:

cpp 复制代码
int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg);
  • thread:这是一个输出参数,用于存储新创建线程的线程 ID。
  • attr:这是一个输入参数,用于设置新线程的属性。如果传递NULL,则新线程将使用默认属性。
  • start_routine:这是一个函数指针,指向新线程将要执行的函数。该函数接受一个void*类型的参数,并返回一个 void* 类型的值。
  • arg:这是传递给start_routine函数的参数。
  • 成功 :返回 0。
  • 失败 :返回错误编号,可以通过pthread_error获取。
cpp 复制代码
#include<iostream>      // 包含标准输入输出流库
#include<pthread.h>     // 包含 POSIX 线程库
#include<unistd.h>      // 包含 UNIX 标准函数库,如 sleep()

using namespace std;   // 使用标准命名空间

// 线程函数
void *newthreadrun(void *args) {
    while (true) {    // 无限循环
        // 输出新线程的信息和进程 ID
        cout << "I am new thread, pid:" << getpid() << endl;
        sleep(1);     // 休眠 1 秒
    }
    // 这个函数不会退出,因此不需要 return 语句
}

int main() {
    pthread_t tid;    // 声明线程 ID 变量

    // 创建新线程,传入线程函数和参数
    pthread_create(&tid, nullptr, newthreadrun, nullptr);

    while (true) {    // 无限循环
        // 输出主线程的信息和进程 ID
        cout << "I am main thread, pid:" << getpid() << endl;
        sleep(1);     // 休眠 1 秒
    }
    // 主循环也不会退出,因此不会执行到这里
    // 如果需要正常退出程序,应该添加适当的退出条件或信号处理
}

5.线程的等待

pthread_join是 POSIX 线程(pthread)库中的一个函数,用于等待线程结束并获取其返回值。以下是关于pthread_join的详细信息:

cpp 复制代码
int pthread_join(pthread_t thread, void **retval);
  • thread:这是要等待的线程的线程 ID。
  • retval:这是一个输出参数,用于存储线程结束时返回的值。如果线程没有返回值,则将其设置为NULL。
  • 成功:返回 0。
  • 失败:返回错误编号,可以通过pthread_error获取。
cpp 复制代码
#include<iostream>
#include<string>
#include<pthread.h>
#include<unistd.h>
//同一个进程内的线程,大部分资源都是共享的,地址空间是共享的
std::string ToHex(pthread_t tid)
{
    char id[64];
    snprintf(id,sizeof(id),"0x%lx",tid);
    return id;
}

using namespace std; 

void *threadrun(void *args)
{
    string threadname=(char*)args;
    int cnt=5; 
    while(cnt)
    {
        cout<<threadname<<"is running:"<<cnt<<",pid:"
        <<getpid()<<"mythread id:"
        <<ToHex(pthread_self())<<endl;
        sleep(1);
        cnt--;
    }
    return (void*)123;
}
//主线程退出==进程退出==所以线程都要退出
//1.往往我们需要main thread最后结束
//2.线程也要被"wait",不然可能内存泄漏
int main()
{
    pthread_t tid;
    pthread_create(&tid,nullptr,threadrun,(void*)"thread-1");

    void* ret=nullptr;
    int n=pthread_join(tid,&ret);
    cout<<"main thread quit,n="<<n<<"main thread get a ret:"<<(int)ret<<endl;
    sleep(5);
    // int cnt=10;
    // while(cnt)
    // {
    //     cout<<"main thread is running:"<<cnt<<",pid:"
    //     <<getpid()<<"new thread id:"<<ToHex(tid)<<" "
    //     <<"main thread id:"<<ToHex(pthread_self())<<endl;
    //     sleep(1);
    //     cnt--;
    // }
    return 0;
}

6.线程的终止

pthread_exit是 POSIX 线程(pthread)库中的一个函数,用于使当前线程立即终止并返回指定的值。以下是关于pthread_exit的详细信息:

cpp 复制代码
void pthread_exit(void *retval);

retval:这是线程退出时返回的值。这个值会被传递给调用pthread_join的线程

pthread_cancel是 POSIX 线程(pthread)库中的一个函数,用于请求取消另一个线程。当一个线程被取消时,它会收到一个取消请求,并可以选择如何响应这个请求。以下是关于pthread_cancel的详细信息:

cpp 复制代码
int pthread_cancel(pthread_t thread);

thread :这是要取消的线程的线程 ID。

cpp 复制代码
const int threadnum = 5; // 定义线程数量为5

// Task类,表示一个任务
class Task {
public:
    Task() {} // 构造函数
    void SetData(int x, int y) { // 设置任务数据
        datax = x;
        datay = y;
    }
    int Excute() { // 执行任务,返回数据x和数据y的和
        return datax + datay;
    }
    ~Task() {} // 析构函数
private:
    int datax; // 任务数据x
    int datay; // 任务数据y
};

// ThreadData类,继承自Task类,用于包装任务和线程名称
class ThreadData : public Task {
public:
    ThreadData(int x, int y, const string &threadname)
        : _threadname(threadname) { // 构造函数,设置线程名称
        _t.SetData(x, y); // 设置任务数据
    }
    string threadname() { // 返回线程名称
        return _threadname;
    }
    int run() { // 执行任务
        return _t.Excute();
    }
private:
    string _threadname; // 线程名称
    Task _t; // 任务数据
};

// Result类,用于存储线程执行结果
class Result {
public:
    Result() {} // 构造函数
    ~Result() {} // 析构函数
    void SetResult(int result, const string &threadname) { // 设置结果和线程名称
        _result = result;
        _threadname = threadname;
    }
    void Print() { // 打印结果
        cout << _threadname << ":" << _result << endl;
    }
private:
    int _result; // 结果
    string _threadname; // 线程名称
};


void *handlerTask(void *args) {
    ThreadData *td = static_cast<ThreadData*>(args); // 获取线程数据
    string name = td->threadname(); // 获取线程名称
    Result* res = new Result(); // 创建结果对象
    int result = td->run(); // 执行任务
    res->SetResult(result, name); // 设置结果和线程名称
    cout << name << "run result:" << result << endl; // 打印结果
    delete td; // 删除线程数据
    sleep(2); // 线程休眠2秒
    return res; // 返回结果对象
}

int main() {
    vector<pthread_t> threads; // 线程ID向量
    for (int i = 0; i < threadnum; i++) { // 创建线程
        char threadname[64]; // 线程名称缓冲区
        snprintf(threadname, 64, "Thread-%d", i + 1); // 格式化线程名称
        ThreadData *td = new ThreadData(10, 20, threadname); // 创建线程数据
        pthread_t tid; // 线程ID
        pthread_create(&tid, nullptr, handlerTask, td); // 创建线程
        threads.push_back(tid); // 添加线程ID到向量
    }

    vector<Result*> result_set; // 结果集合
    void *ret = nullptr;
    for (auto& tid : threads) { // 等待每个线程完成
        pthread_join(tid, &ret); // 等待线程完成
        Result* res = static_cast<Result*>(ret); // 获取返回的结果对象
        result_set.push_back(res); // 添加结果对象到集合
    }

    for (auto& res : result_set) { // 打印每个线程的结果
        res->Print(); // 打印结果
        delete res; // 删除结果对象
    }
}

7.线程的分离

在Linux环境中,pthread_detach函数用于设置线程的分离属性。分离线程是指线程在完成其工作后立即退出,而不等待其他线程来收集它的返回值或清理它的资源。以下是关于pthread_detach的详细信息:

cpp 复制代码
int pthread_detach(pthread_t thread);

当一个线程被设置为分离状态并完成其工作后,它会立即退出,并且不会返回任何值给调用pthread_join的线程。这意味着调用pthread_join的线程不会接收到任何值,也不会被阻塞。

2.线程的同步和互斥

线程互斥是指操作系统提供的一种机制,用于确保多个线程在访问共享资源时能够互不干扰,从而避免数据竞争和不一致性。线程互斥通常通过以下几种方式实现:

下面是封装了一个简单的线程:

cpp 复制代码
#ifndef __THREAD_HPP__
#define __THREAD_HPP__

#include <iostream>
#include <string>
#include <unistd.h>
#include <functional>
#include <pthread.h>

namespace ThreadModule
{
    // 定义了一个模板函数类型,用于线程执行函数
    template<typename T>
    using func_t = std::function<void(T&)>; // 使用引用传递,允许修改传入的数据

    // 线程类模板
    template<typename T>
    class Thread
    {
    public:
        // 执行传入的函数对象
        void Excute()
        {
            _func(_data);
        }

    public:
        // 构造函数,初始化线程所需的数据和函数
        Thread(func_t<T> func, T &data, const std::string &name="none-name")
            : _func(func), _data(data), _threadname(name), _stop(true)
        {}

        // 静态成员函数,作为线程的执行函数
        static void *threadroutine(void *args) // 类成员函数,形参是有this指针的!!
        {
            Thread<T> *self = static_cast<Thread<T> *>(args); // 将void*指针转换为Thread<T>指针
            self->Excute(); // 调用Excute函数执行传入的函数对象
            return nullptr;
        }

        // 启动线程
        bool Start()
        {
            int n = pthread_create(&_tid, nullptr, threadroutine, this); // 创建线程
            if(!n) // 如果创建成功
            {
                _stop = false; // 设置停止标志为false
                return true;
            }
            else
            {
                return false;
            }
        }

        // 分离线程,线程结束后资源会被自动回收
        void Detach()
        {
            if(!_stop) // 如果线程未停止
            {
                pthread_detach(_tid); // 分离线程
            }
        }

        // 等待线程结束
        void Join()
        {
            if(!_stop) // 如果线程未停止
            {
                pthread_join(_tid, nullptr); // 等待线程结束
            }
        }

        // 获取线程名称
        std::string name()
        {
            return _threadname;
        }

        // 设置线程停止标志
        void Stop()
        {
            _stop = true;
        }

        // 析构函数
        ~Thread() {}

    private:
        pthread_t _tid; // 线程ID
        std::string _threadname; // 线程名称
        T &_data;  // 引用传递的数据,所有线程可以访问同一个数据
        func_t<T> _func; // 函数对象,线程执行的函数
        bool _stop; // 线程停止标志
    };
} // namespace ThreadModule

#endif

1.互斥锁

互斥锁(Mutex)是一种同步机制,用于保护共享资源,防止多个线程同时访问。以下是关于互斥锁的详细信息:

  • 程序在访问共享资源之前,必须先获得互斥锁。
  • 只有当线程持有互斥锁时,它才能访问共享资源。
  • 线程在完成对共享资源的访问后,必须释放互斥锁,以便其他线程可以获取它。
  • 互斥锁通常用于保护临界区,即那些只允许一个线程访问的代码段。
  • 在多线程编程中,互斥锁是确保数据一致性和防止竞态条件的关键。

临界区的主要特点是:

  1. 共享资源:临界区访问的是共享资源,即多个线程可以访问的资源。

  2. 互斥访问:为了避免竞态条件和其他同步问题,临界区内的代码必须保证在同一时刻只有一个线程可以执行。

  3. 执行时间:临界区的执行时间应该尽可能短,以减少线程的阻塞和等待时间。

pthread_mutex_lock是 POSIX 线程(pthread)库中的一个函数,用于获取互斥锁。当一个线程尝试访问一个共享资源时,它必须首先获取互斥锁。以下是关于pthread_mutex_lock的详细信息:

cpp 复制代码
int pthread_mutex_lock(pthread_mutex_t *mutex);

mutex :这是指向pthread_mutex_t类型的指针,用于指定要获取的互斥锁。

pthread_mutex_unlock是 POSIX 线程(pthread)库中的一个函数,用于释放互斥锁。当一个线程完成对共享资源的访问后,它必须释放互斥锁,以便其他线程可以获取它。以下是关于pthread_mutex_unlock的详细信息:

cpp 复制代码
int pthread_mutex_unlock(pthread_mutex_t *mutex);

2.条件变量

条件变量(Condition Variable)是操作系统提供的一种同步机制,用于线程间的通信。它通常与互斥锁一起使用,以确保线程在等待某些条件满足时不会竞争共享资源。以下是关于条件变量的详细信息:

  • 当线程需要等待某些条件满足时,它会释放当前持有的互斥锁,然后等待条件变量。
  • 当其他线程满足这些条件时,它会通知等待的线程,然后等待的线程会重新获取互斥锁并继续执行。

pthread_cond_init是 POSIX 线程(pthread)库中的一个函数,用于初始化一个条件变量。条件变量是线程间通信的同步机制,它允许线程在等待某些条件满足时不会竞争共享资源。

cpp 复制代码
int pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *cond_attr);
  • cond:这是指向pthread_cond_t类型的指针,用于指定要初始化的条件变量。
  • cond_attr:这是一个可选参数,指向pthread_condattr_t类型的指针,用于指定条件变量的属性。如果传递NULL,则使用默认属性。

pthread_cond_destory是 POSIX 线程(pthread)库中的一个函数,用于清理条件变量。当一个条件变量不再需要时,可以使用pthread_cond_destory函数来释放它所占用的资源。

cpp 复制代码
int pthread_cond_destroy(pthread_cond_t *cond);

pthread_cond_wait是 POSIX 线程(pthread)库中的一个函数,用于线程在等待条件变量时释放互斥锁。当一个线程需要等待某个条件满足时,它会释放当前持有的互斥锁,然后等待条件变量。

cpp 复制代码
int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);
  • cond:这是指向pthread_cond_t类型的指针,用于指定要等待的条件变量。
  • mutex:这是指向pthread_mutex_t类型的指针,用于指定当前持有的互斥锁。

pthread_cond_signal是 POSIX 线程(pthread)库中的一个函数,用于唤醒一个等待条件变量的线程。当一个线程满足某些条件时,它可以使用pthread_cond_signa函数来通知其他等待的线程。

cpp 复制代码
int pthread_cond_signal(pthread_cond_t *cond);
  • cond:这是指向pthread_cond_t类型的指针,用于指定要发送信号的条件变量。

pthread_cond_broadcast是 POSIX 线程(pthread)库中的一个函数,用于唤醒所有等待特定条件变量的线程。当一个线程满足某些条件时,它可以使用pthread_cond_broadcast函数来通知所有等待的线程。

cpp 复制代码
int pthread_cond_broadcast(pthread_cond_t *cond);

下面是一段测试代码:

cpp 复制代码
#include<iostream>
#include<string>
#include<pthread.h>
#include<vector>
#include<unistd.h>

pthread_cond_t gcond=PTHREAD_COND_INITIALIZER;
pthread_mutex_t gmutex=PTHREAD_MUTEX_INITIALIZER;

void *SlaverCore(void *args)
{
    std::string name=static_cast<const char*>(args);
    while(true)
    {
        pthread_mutex_lock(&gmutex);
        pthread_cond_wait(&gcond,&gmutex);
        std::cout<<"当前被叫醒的线程是:"<<name<<std::endl;
        pthread_mutex_unlock(&gmutex);
    }
}

void *MasterCore(void *args)
{
    sleep(3);
    std::cout<<"master开始工作..."<<std::endl;
    std::string name=static_cast<const char*>(args);
    while(true)
    {
        pthread_cond_signal(&gcond);
        std::cout<<"master唤醒一个线程..."<<std::endl;
        sleep(1);
    }
}

void StartMaster(std::vector<pthread_t> *tidsptr)
{
    pthread_t tid;
    int n=pthread_create(&tid,nullptr,MasterCore,(void*)"Master Thread");
    if(n==0)
    {
        std::cout<<"create success"<<std::endl;
    }
    tidsptr->emplace_back(tid);
}

void StartSlaver(std::vector<pthread_t> *tidsptr,int threadnum=3)
{
    for(int i=0;i<threadnum;i++)
    {
        char *name=new char[64];
        snprintf(name,64,"slaver-%d",i+1);
        pthread_t tid;
        int n=pthread_create(&tid,nullptr,SlaverCore,name);
        if(n==0)
        {
            std::cout<<"create success:"<<name<<std::endl;
            tidsptr->emplace_back(tid);
        }
    }
}

void WaitThread(std::vector<pthread_t> &tids)
{
    for(auto &tid:tids)
    {
        pthread_join(tid,nullptr);
    }
}

int main()
{
    std::vector<pthread_t> tids;
    StartMaster(&tids);
    StartSlaver(&tids,5);
    WaitThread(tids);
    return 0;
}

3.生产消费模型

生产消费模型是多线程编程中的一个经典场景,它描述了生产者线程和消费者线程之间如何共享数据缓冲区。在这个模型中,生产者线程负责创建数据并将其放入缓冲区,而消费者线程负责从缓冲区中取出数据并处理。

以下是生产消费模型的关键组成部分:

  1. 缓冲区:这是一个共享的资源,用于存储数据。它可以是一个固定大小的数组、链表或其他数据结构。

  2. 生产者线程:负责创建数据并将其放入缓冲区。生产者线程需要确保在缓冲区满时不会写入数据,以避免数据丢失。

  3. 消费者线程:负责从缓冲区中取出数据并处理。消费者线程需要确保在缓冲区空时不会读取数据,以避免空指针异常。

  4. 同步机制:为了确保生产者线程和消费者线程之间的同步,可以使用互斥锁、条件变量等同步机制。

在生产消费模型中,生产者线程和消费者线程之间通常存在以下关系:

  • 生产者线程在缓冲区未满时写入数据。
  • 消费者线程在缓冲区非空时读取数据。
  • 生产者线程和消费者线程之间通过互斥锁和条件变量进行同步。

4.阻塞队列

阻塞队列(Blocking Queue)是一种特殊的队列,它提供了一种机制,使得生产者和消费者线程可以以不同的速度工作,而不会造成资源浪费或死锁。当队列满时,生产者线程会被阻塞,直到队列中有空闲空间;当队列空时,消费者线程会被阻塞,直到队列中有数据可消费。

以下是阻塞队列的一些关键特性:

  1. 生产者-消费者模型:阻塞队列支持生产者线程和消费者线程之间的数据传递。生产者线程将数据放入队列,而消费者线程从队列中取出数据。

  2. 线程安全:阻塞队列通常实现线程安全,可以被多个生产者和消费者线程同时访问。

  3. 阻塞机制:当队列满时,生产者线程会被阻塞,直到队列中有空闲空间;当队列空时,消费者线程会被阻塞,直到队列中有数据可消费。

  4. 无界队列和有界队列:阻塞队列可以是无界队列或有限大小的队列。无界队列没有容量限制,而有限大小的队列有最大容量限制。

cpp 复制代码
#ifndef __BLOCK_QUEUE_HPP__
#define __BLOCK_QUEUE_HPP__

#include <iostream>
#include <string>
#include <queue>
#include <pthread.h>

template <class T>
class BlockQueue
{
private:
    bool IsFull()
    {
        return _block_queue.size() == _cap;
    }
    bool IsEmpty()
    {
        return _block_queue.empty();
    }
public:
    BlockQueue(int cap) : _cap(cap)
    {
        pthread_mutex_init(&_mutex, nullptr);
        pthread_cond_init(&_product_cond, nullptr);
        pthread_cond_init(&_consum_cond, nullptr);
    }
    void Enqueue(T &in) // 生产者用的接口
    {
        pthread_mutex_lock(&_mutex);
        if(IsFull()) //BUG??
        {
            // 生产线程去等待,是在临界区中休眠的!你现在还持有锁呢!!!
            // 1. pthread_cond_wait调用是: a. 让调用进程等待 b. 自动释放曾经持有的_mutex锁
            pthread_cond_wait(&_product_cond, &_mutex); 
        }
        // 进行生产
        // _block_queue.push(std::move(in));
        std::cout << in << std::endl;
        _block_queue.push(in);
        // 通知消费者来消费
        pthread_cond_signal(&_consum_cond);
        pthread_mutex_unlock(&_mutex);
    }
    void Pop(T *out) // 消费者用的接口
    {
        pthread_mutex_lock(&_mutex);
        if(IsEmpty())
        {
            // 消费线程去等待,是在临界区中休眠的!你现在还持有锁呢!!!
            // 1. pthread_cond_wait调用是: a. 让调用进程等待 b. 自动释放曾经持有的_mutex锁
            pthread_cond_wait(&_consum_cond, &_mutex); 
        }

        // 进行消费
        *out = _block_queue.front();
        _block_queue.pop();
        // 通知生产者来生产
        pthread_cond_signal(&_product_cond);
        pthread_mutex_unlock(&_mutex);
    }
    ~BlockQueue()
    {
        pthread_mutex_destroy(&_mutex);
        pthread_cond_destroy(&_product_cond);
        pthread_cond_destroy(&_consum_cond);
    }

private:
    std::queue<T> _block_queue;   // 阻塞队列
    int _cap;                     // 总上限
    pthread_mutex_t _mutex;       // 保护_block_queue的锁
    pthread_cond_t _product_cond; // 专门给生产者提供的条件变量
    pthread_cond_t _consum_cond;  // 专门给消费者提供的条件变量
};

#endif

5.信号量

在多线程编程中,信号量(Semaphore)是一种同步机制,用于控制对共享资源的访问。信号量可以用来实现互斥锁,也可以用来实现线程间的同步。

  • 信号量的值可以增加(通常使用pthread_sem_post)或减少(通常使用 pthread_sem_wait)。
  • 当信号量的值大于0时,线程可以访问共享资源;当信号量的值小于或等于0时,线程必须等待。
  • 信号量的值必须始终为非负整数。

pthread_sem_init是 POSIX 线程(pthread)库中的一个函数,用于初始化一个信号量。信号量是一种同步机制,用于控制对共享资源的访问。它可以用来实现互斥锁,也可以用来实现线程间的同步。

cpp 复制代码
int pthread_sem_init(pthread_sem_t *sem, const pthread_semattr_t *sem_attr, unsigned int value);
  • em:这是指向pthread_sem_t类型的指针,用于指定要初始化的信号量。
  • sem_attr:这是一个可选参数,指向pthread_semattr_t类型的指针,用于指定信号量的属性。如果传递NULL,则使用默认属性。
  • value:这是信号量的初始值。如果传递0,则信号量会被初始化为一个空信号量。

pthread_sem_post是 POSIX 线程(pthread)库中的一个函数,用于增加信号量的值。

cpp 复制代码
int pthread_sem_post(pthread_sem_t *sem);
  • sem:这是指向pthread_sem_t类型的指针,用于指定要增加值的信号量。

pthread_sem_wait是 POSIX 线程(pthread)库中的一个函数,用于减少信号量的值。

cpp 复制代码
int pthread_sem_wait(pthread_sem_t *sem);
  • sem:这是指向pthread_sem_t类型的指针,用于指定要减少值的信号量。

6.唤醒队列

在多线程编程中,当一个线程因为某种原因(如等待条件变量、同步机制等)而被阻塞时,它会进入一个特定的等待队列中。这个等待队列被称为唤醒队列(Wakeup Queue),它包含了所有等待某些事件的线程。当这些事件发生时,这些线程会被唤醒,并有机会继续执行。

唤醒队列通常与条件变量、信号量等同步机制一起使用,以确保线程在等待事件时不会竞争共享资源。当事件发生时,其他线程可以通知等待的线程,然后等待的线程会从唤醒队列中移除,并有机会继续执行。

以下是一些常见的唤醒队列实现:

  1. 条件变量唤醒队列:当条件变量被信号或信号量通知时,所有等待该条件变量的线程都会被唤醒,并重新尝试获取互斥锁。

  2. 信号量唤醒队列:当信号量的值被增加时,所有等待该信号量的线程都会被唤醒,并有机会继续执行。

  3. 等待队列:操作系统内核中通常有一个全局的等待队列,用于管理所有等待事件的线程。当事件发生时,内核会遍历这个等待队列,并唤醒所有符合条件的线程。

相关推荐
运维&陈同学几秒前
【zookeeper01】消息队列与微服务之zookeeper工作原理
运维·分布式·微服务·zookeeper·云原生·架构·消息队列
是阿建吖!1 分钟前
【Linux】进程状态
linux·运维
二进制_博客7 分钟前
Flink学习连载文章4-flink中的各种转换操作
大数据·学习·flink
hzyyyyyyyu15 分钟前
内网安全隧道搭建-ngrok-frp-nps-sapp
服务器·网络·安全
codebolt29 分钟前
ADS学习记录
学习
明明跟你说过30 分钟前
Linux中的【tcpdump】:深入介绍与实战使用
linux·运维·测试工具·tcpdump
Komorebi.py2 小时前
【Linux】-学习笔记05
linux·笔记·学习
Mr_Xuhhh2 小时前
重生之我在学环境变量
linux·运维·服务器·前端·chrome·算法
中云DDoS CC防护蔡蔡3 小时前
微信小程序被攻击怎么选择高防产品
服务器·网络安全·微信小程序·小程序·ddos