cen's blog cen's blog
首页
  • 编程文章

    • markdown使用
  • 学习笔记

    • C++学习
    • C++数据结构
    • MySQL
    • Linux
    • 网络编程
算法
  • Git
  • ProtoBuf
  • 分类
  • 标签
  • 归档
关于
GitHub (opens new window)

cen

十年饮冰,难凉热血
首页
  • 编程文章

    • markdown使用
  • 学习笔记

    • C++学习
    • C++数据结构
    • MySQL
    • Linux
    • 网络编程
算法
  • Git
  • ProtoBuf
  • 分类
  • 标签
  • 归档
关于
GitHub (opens new window)
  • 类和对象
  • 内存管理
  • 泛型模板
  • string
  • vector
  • list
  • stack和queue
  • priority_queue
  • 继承
  • 多态
  • set和map
  • bitset
  • C++11
  • 异常
  • 智能指针
  • 特殊类设计
  • function和bind
  • 右值引用和移动语义
  • thread
    • thread
    • this_thread 命名空间
    • 互斥量库
      • mutex
      • lock_guard
      • unique_lock
    • 条件变量库
    • 多线程编程
  • C++学习笔记
cen
2025-11-15
目录

thread

# thread

thread 对线程进行了支持,使得 C++在并行编程时不需要依赖第三方库,增强可移植性。

成员函数 功能
join 对该线程进行等待,在等待的线程返回之前,调用 join 函数的线程将会被阻塞
joinable 判断该线程是否已经执行完毕,如果是则返回 true,否则返回 false
detach 将该线程与创建线程进行分离,被分离后的线程不再需要创建线程调用 join 函数对其进行等待
swap 将两个线程对象关联线程的状态进行交换
  • join:主线程创建新线程后,可以调用 join 函数等待新线程终止,当新线程终止时 join 函数就会自动清理线程相关的资源。
  • detach:将新线程与主线程进行分离,分离后新线程会在后台运行,其所有权和控制权将会交给 C++运行库,此时 C++运行库会保证当线程退出时,其相关资源能够被正确回收。

# this_thread 命名空间

函数名 功能
get_id 获取该线程的 id
yield 当前线程“放弃”执行,让操作系统调度另一线程继续执行
sleep_until 让当前线程休眠到一个具体时间点
sleep_for 让当前线程休眠一个时间段

# 互斥量库

# mutex

int tickets = 100;
mutex m;

void sellTicket() {
    while (tickets > 0) {
        m.lock();
        if(tickets > 0) {
            cout << tickets << endl;
            tickets--;
        }
        m.unlock();
        this_thread::sleep_for(std::chrono::microseconds(100));
    }
}

void TestThread() {
    vector<thread> threads;

    for(int i = 0;i < 3;++i) {
        threads.push_back(thread(sellTicket));
    }

    for(int i = 0;i < 3;++i) {
        threads[i].join();
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

使用 mutex 时,如果加锁的范围太大,可能在中途出错没有解锁,此后申请这个互斥锁的线程就会被阻塞住,造成了死锁问题,推荐使用 lock_guard 和 unique_lock。

# lock_guard

lock_guard 类模板主要是通过 RAII 的方式,对其管理的互斥锁进行了封装。

在需要加锁的地方,用互斥锁实例化一个 lock_guard 对象,在 lock_guard 的构造函数中会调用 lock 进行加锁,当 lock_guard 对象出作用域前会调用析构函数,在 lock_guard 的析构函数中会调用 unlock 自动解锁。

void sellTicket() {
    while (tickets > 0) {
        {
            std::lock_guard<std::mutex> lock(m);
            if(tickets > 0) {
                std::cout << tickets << std::endl;
                tickets--;
            }
        }
        std::this_thread::sleep_for(std::chrono::microseconds(100));
    }
}
1
2
3
4
5
6
7
8
9
10
11
12

# unique_lock

lock_guard 不能用在函数参数传递和返回过程中,C++11 提供了 unique_lock,与 wait 结合使用。

# 条件变量库

  • wait
void wait(unique_lock<mutex>& lck);
1

wait 函数一般在临界区中调用的,为了让当前线程调用 wait 阻塞时其他线程能够获取到锁,因此调用 wait 系列函数时需要传入一个互斥锁,当线程被阻塞时这个互斥锁会被自动解锁,而当这个线程被唤醒时,又会自动获得这个互斥锁。因此 wait 系列函数实际上有两个功能,一个是让线程在条件不满足时进行阻塞等待,另一个是让线程将对应的互斥锁进行解锁。

  • notify 系列成员函数的作用就是唤醒等待的线程,包括 notify_one 和 notify_all。条件变量下可能会有多个线程在进行阻塞等待,这些线程由等待状态转为阻塞状态获取互斥锁之后,继续执行。
  1. notify_one:唤醒等待队列中的首个线程,如果等待队列为空则什么也不做。
  2. notify_all:唤醒等待队列中的所有线程,如果等待队列为空则什么也不做。

使用规范:

等待条件变量的代码:

pthread_mutex_lock(&mutex);
while (条件为假)
	pthread_cond_wait(&cond, &mutex);
修改条件
pthread_mutex_unlock(&mutex);
1
2
3
4
5

唤醒等待线程的代码:

pthread_mutex_lock(&mutex);
设置条件为真
pthread_cond_signal(&cond);
pthread_mutex_unlock(&mutex);
1
2
3
4

# 多线程编程

多线程编程的两个问题:

  1. 线程的互斥:静态条件-》临界区-》mutex/CAS
  2. 线程的同步:信号量/条件变量

示例:线程间同步通信,生产者消费者

class Queue {
public:
    Queue(int max_size = 20) : max_size_(max_size) {}

    void Push(int &data) {
        std::unique_lock<std::mutex> lock(m_);
        while (IsFull()) {
            // producer waiting...
            p_cond_.wait(lock);
        }
        q_.push(data);
        std::cout << "producer:" << data << std::endl;
        c_cond_.notify_one();
    }

    void Pop() {
        std::unique_lock<std::mutex> lock(m_);
        while (IsEmpty()) {
            c_cond_.wait(lock);
        }
        std::cout << "consumer:" << q_.front() << std::endl;
        q_.pop();
        p_cond_.notify_one();
    }

    int Front() const {
        return q_.front();
    }

    bool IsEmpty() const {
        return q_.empty();
    }

    bool IsFull() const {
        return q_.size() == max_size_;
    }

private:
    int max_size_;
    std::queue<int> q_;
    std::mutex m_;
    std::condition_variable p_cond_;
    std::condition_variable c_cond_;
};

void consumer(Queue *q) {
    for (int i = 0; i < 5; ++i) {
        q->Pop();
        std::this_thread::sleep_for(std::chrono::microseconds(100));
    }
}

void producer(Queue *q) {
    for (int i = 0; i < 5; ++i) {
        int data = rand() % 10 + 1;
        q->Push(data);
        std::this_thread::sleep_for(std::chrono::microseconds(100));
    }
}

int main() {
    Queue *queue = new Queue(30);
    std::thread c(consumer, queue);
    std::thread p(producer, queue);

    // ...

    c.join();
    p.join();
    return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
右值引用和移动语义

← 右值引用和移动语义

最近更新
01
Cmake
11-29
02
动态规划
11-08
03
Protobuf
09-28
更多文章>
Theme by Vdoing | Copyright © 2024-2026 京ICP备2020044002号-3 京公网安备11010502056119号
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式