C++多线程编程技巧

随着多核处理器的普及,多线程编程已经成为现代C++开发中不可或缺的技能。本文将详细介绍C++多线程编程的核心概念和最佳实践。

1. 线程基础

1.1 创建和管理线程

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
#include <thread>
#include <iostream>

void worker(int n) {
    std::cout << "Thread " << n << " is running\n";
}

int main() {
    std::thread t1(worker, 1);
    std::thread t2(worker, 2);
    
    t1.join();  // 等待线程完成
    t2.join();
    
    return 0;
}

1.2 线程传参

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
class Task {
public:
    void execute(std::string& msg, int count) {
        for(int i = 0; i < count; ++i) {
            std::cout << msg << std::endl;
        }
    }
};

Task task;
std::string msg = "Hello";
std::thread t(&Task::execute, &task, std::ref(msg), 3);
t.join();

2. 互斥量和锁

2.1 基本互斥量使用

1
2
3
4
5
6
7
8
9
#include <mutex>

std::mutex mtx;
int shared_data = 0;

void increment() {
    std::lock_guard<std::mutex> lock(mtx);
    ++shared_data;
}

2.2 死锁预防

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
std::mutex mutex1, mutex2;

void deadlock_prone() {
    // 错误方式:可能导致死锁
    std::lock_guard<std::mutex> lock1(mutex1);
    std::lock_guard<std::mutex> lock2(mutex2);
}

void deadlock_safe() {
    // 正确方式:使用std::lock
    std::lock(mutex1, mutex2);
    std::lock_guard<std::mutex> lock1(mutex1, std::adopt_lock);
    std::lock_guard<std::mutex> lock2(mutex2, std::adopt_lock);
}

3. 条件变量

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
#include <condition_variable>

std::mutex mtx;
std::condition_variable cv;
bool ready = false;

void producer() {
    std::unique_lock<std::mutex> lock(mtx);
    ready = true;
    cv.notify_one();
}

void consumer() {
    std::unique_lock<std::mutex> lock(mtx);
    cv.wait(lock, [] { return ready; });
    // 处理数据
}

4. 原子操作

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
#include <atomic>

std::atomic<int> counter(0);

void increment() {
    ++counter;  // 原子操作,无需互斥锁
}

void atomic_operations() {
    counter.fetch_add(1);  // 原子加法
    counter.fetch_sub(1);  // 原子减法
    counter.exchange(5);   // 原子交换
}

5. 线程池实现

 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
class ThreadPool {
private:
    std::vector<std::thread> workers;
    std::queue<std::function<void()>> tasks;
    std::mutex queue_mutex;
    std::condition_variable condition;
    bool stop;

public:
    ThreadPool(size_t threads) : stop(false) {
        for(size_t i = 0; i < threads; ++i) {
            workers.emplace_back([this] {
                while(true) {
                    std::function<void()> task;
                    {
                        std::unique_lock<std::mutex> lock(queue_mutex);
                        condition.wait(lock, [this] {
                            return stop || !tasks.empty();
                        });
                        if(stop && tasks.empty()) return;
                        task = std::move(tasks.front());
                        tasks.pop();
                    }
                    task();
                }
            });
        }
    }

    template<class F>
    void enqueue(F&& f) {
        {
            std::unique_lock<std::mutex> lock(queue_mutex);
            tasks.emplace(std::forward<F>(f));
        }
        condition.notify_one();
    }

    ~ThreadPool() {
        {
            std::unique_lock<std::mutex> lock(queue_mutex);
            stop = true;
        }
        condition.notify_all();
        for(std::thread &worker: workers) {
            worker.join();
        }
    }
};

6. 异步任务

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
#include <future>

std::future<int> calculate_async() {
    return std::async(std::launch::async, []() {
        // 耗时计算
        return 42;
    });
}

void use_async() {
    auto future = calculate_async();
    // 做其他工作
    int result = future.get();  // 获取结果
}

7. 读写锁

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
#include <shared_mutex>

class ThreadSafeCounter {
    mutable std::shared_mutex mutex_;
    int value_ = 0;

public:
    // 写操作使用独占锁
    void increment() {
        std::unique_lock<std::shared_mutex> lock(mutex_);
        ++value_;
    }

    // 读操作使用共享锁
    int get() const {
        std::shared_lock<std::shared_mutex> lock(mutex_);
        return value_;
    }
};

最佳实践建议

  1. 优先使用高级同步原语(如std::async)而不是直接管理线程
  2. 使用RAII风格的锁管理(如std::lock_guard)
  3. 避免使用全局变量存储线程间共享数据
  4. 合理使用原子操作代替互斥量
  5. 注意防止死锁和竞态条件

性能优化技巧

  1. 减少锁的粒度
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
class Optimized {
    std::mutex mutex_;
    std::vector<int> data_;

public:
    void add(int value) {
        // 在锁外准备数据
        int new_value = process(value);
        {
            std::lock_guard<std::mutex> lock(mutex_);
            data_.push_back(new_value);
        }
    }
};
  1. 使用无锁数据结构
1
2
3
4
5
6
7
8
9
template<typename T>
class LockFreeQueue {
    struct Node {
        T data;
        std::atomic<Node*> next;
    };
    std::atomic<Node*> head_;
    std::atomic<Node*> tail_;
};

总结

C++多线程编程是一个复杂但重要的主题。通过合理使用互斥量、条件变量、原子操作等同步原语,我们可以构建高效的并发程序。关键是要理解并发编程的核心概念,遵循最佳实践,并在实践中不断积累经验。

使用绝夜之城强力驱动