开发者

c++ 标准库多线程问题小结

开发者 https://www.devze.com 2025-04-11 10:58 出处:网络 作者: 云山漫卷
目录1. 创建线程2. 传递参数给线程函数3. 线程同步4. 使用 std::lock_guard 自动管理锁5. 条件变量6. 线程池7. 线程局部存储8. 异步任务总结C++ 多线程编程允许程序同时执行多个任务,从而提高性能和响应能
目录
  • 1. 创建线程
  • 2. 传递参数给线程函数
  • 3. 线程同步
  • 4. 使用 std::lock_guard 自动管理锁
  • 5. 条件变量
  • 6. 线程池
  • 7. 线程局部存储
  • 8. 异步任务
  • 总结

C++ 多线程编程允许程序同时执行多个任务,从而提高性能和响应能力。C++11 引入了 <thread> 库,使得多线程编程更加方便。以下是一些基本概念和示例,帮助你理解如何在 C++ 中进行多线程编程。

1. 创建线程

使用 std::thread 类可以创建一个新线程。你需要将一个函数或可调用对象传递给 std::thread 构造函数。

#include <IOStream>
#include <thread>
void threadFunction() {
    std::cout << "Hello from thread!\\\\n";
}
int main() {
    std::thread t(threadFunction);  // 创建线程并执行 threadFunction
    t.join();  // 等待线程结束
    std::cout << "Hello from main!\\\\n";
    return 0;
}

2. 传递参数给线程函数

你可以通过 std::thread 构造函数传递参数给线程函数。

#include <iostream>
#include <thread>
void printMessage(const std::string& message) {
    std::cout << message << "\\\\n";
}
int main() {
    std::thread t(printMessage, "Hello from thread!");
    t.join();
    std::cout << "Hello from main!\\\\n";
    return 0;
}

3. 线程同步

多个线程可能会同时访问共享资源,导致数据竞争。为了避免这种情况,可以使用互斥锁(std::mutex)来保护共享资源。

#include <iostream>
#include <thread>
#include <mutex>
#include <vector>
std::mutex mtx;  // 互斥锁
void printNumber(int num) {
    mtx.lock();  // 加锁
    std::cout << "Number: " << num << "\\\\n";
    mtx.unlock();  // 解锁
}
int main() {
    std::vector<std::thread> threads;
    for (int i = 0; i < 10; ++i) {
        threads.emplace_back(printNumber, i);
    }
    for (auto& t : threads) {
        t.join();
    }
    return 0;
}

4. 使用 std::lock_guard 自动管理锁

std::lock_guard 是一个 RAII 风格的简单的锁管理器,它在构造时自动加锁,在析构时自动解锁。

#include <iostream>
#include <thread>
#include <mutex>
#include <vector>
std::mutex mtx;
void printNumber(int num) {
    std::lock_guard<std::mutex> lock(mtx);  // 自动加锁和解锁
    std::cout << "Number: " << num << "\\\\n";
}
int main() {
    std::vector<std::thread> threads;
    for (int i = 0; i < 10; ++i) {
        threads.emplace_back(printNumber, i);
    }
    for (auto& t : threads) {
        t.join();
    }
    returpythonn 0;
}

5. 条件变量

条件变量(std::condition_variable)用于线程间的同步,允许一个线程等待另一个线程满足某些条件。

配合std::condition_variable::wait() 函数的第一个参数的必须是比lock_guard更灵活控制也更复杂重度的锁:std::unique_lock。它可以RAII自动析构,也可以手动lock/unlock,中间有的代码段就可以释放锁。手动把它unlock之后只是解锁,没有销毁,后续可以按需复用再次 lock/unlock。

#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
std::mutex mtx;
std::condition_variable cv;
bool ready = false;
void printMessage() {
    std::**unique_lock**<std::mutex> lo编程ck(mtx);
    cv.wait(lock, []{ return ready; });  // 等待条件满足
    std::cout << "Hello from thread!\\\\n";
}
int main() {
    std::thread t(printMessage);
    {
        std::lock_guard<std::mutex> lock(mtx);
        ready = true;  // 设置条件为 true
    }
    cv.notify_one();  // 通知等待的线程
    t.join();
    std::cout << "Hello from main!\\\\n";
    return 0;
}
相比lock_guard的优势:
1. 灵活编程客栈性:unique_lock 支持延迟锁定(可以先构造对象而不立即加锁),而 lock_guard 在构造时就必须加锁。这意味着你可以先创建 unique_lock 对象,然后根据程序逻辑需要时再调用 lock() 或 unlock() 方法进行手动加锁或解锁。
2. 条件变量的支持:unique_lock 可以与标准库中的条件变量一起使用,如 std::condition_variable,这是 lock_guard 所不具备的功能。这是因为条件变量需要能够原子地释放锁并进入等待状态,这正是 unique_lock 提供的能力之一。
3. 锁的所有权转移:unique_lock 支持移动语义(move semantics),允许将锁的所有权从一个 unique_lock 对象转移到另一个对象,从而使得锁可以在不同的作用域中传递。而 lock_guard 不支持这种操作,它的锁所有权是固定的。
4. 尝试锁定(try-locking):除了基本的 lock() 和 unlock() 方法外,unique_lock 还提供了 try_lock() 方法,该方法尝试获取锁但不会阻塞线程,如果无法获得锁则立即返回失败结果。这对于避免线程长时间阻塞非常有用。
wait第二个参数predicate谓词的用法参见:
<https://en.cppreference.com/w/cpp/thread/condition_variable/wait>
predicate不满足不会结束等待执行后续语句。

6. 线程池

C++ 标准库没有直接提供线程池的实现,但你可以使用第三方库(如 Boost)或自己实现一个简单的线程池。

#include <iostream>
#include <thread>
#include <vector>
#include <queue>
#include <mutex>
#include <condition_variable>
#include <functional>
class ThreadPool {
public:
    ThreadPool(size_t numThreads) {
        for (size_t i = 0; i < numThreads; ++i) {
            workers.emplace_back([this] {
                while (true) {
                    std::function<void()> task;
                    {
                        std::unique_lock<std::mutex> lock(this->queueMutex);
                        编程客栈this->condition.wait(lock, [this] { return this->stop || !this->tasks.empty(); });
                        if (this->stop && this->tasks.empty()) return;
                        task = std::move(this->tasks.front());
                        this->tasks.pop();
                    }
                    task();
                }
            });
        }
    }
    template<class F>
    void enqueue(F&& f) {
        {
            std::unique_lock<std::mutex> lock(queueMutex);
            tasks.emplace(std::forward<F>(f));
        }
        condition.notify_one();
    }
    ~ThreadPool() {
        {
            std::unique_lock<std::mutex> lock(queueMutex);
            stop = true;
        }
        condition.notify_all();
        for (std::thread &worker : workers) {
            worker.join();
        }
    }
private:
    std::vector<std::thread> workers;
    std::queue<std::function<void()>> tasks;
    std::mutex queueMutex;
    std::condition_variable condition;
    bool stop = false;
};
int main() {
    ThreadPool pool(4);
    for (int i = 0; i < 8; ++i) {
        pool.enqueue([i] {
            std::cout << "Task " << i << " is running on thread " << std::this_thread::get_id() << "\\\\n";
        });
    }
    return 0;
}

7. 线程局部存储

线程局部存储(Thread Local Storage, TLS)允许每个线程拥有自己的变量实例。C++11 引入了 thread_local 关键字来实现这一点。

#include <iostream>
#include <thread>
thread_local int threadLocalVar = 0;
void threadFunction(int id) {
    threadLocalVar = id;
    std::cout << "Thread " << id << " has threadLocalVar = " << threadLocalVar << "\\\\n";
}
int main() {
    std::thread t1(threadFunction, 1);
    std::thread t2(threadFunction, 2);
    t1.join();
    t2.join();
    return 0;
}

8. 异步任务

C++11 还引入了 std::async 和 std::future,用于异步执行任务并获取结果。

#include <iostream>
#include <future>
int compute() {
    std::this_thread::sleep_for(std::chrono::seconds(2));
    return 42;
}
int main() {
    // 启动异步任务
    std::future<int> fut = std::async(std::launch::async, compute);
    // 获取结果
    int result = fut.get();
    std::cout << "Result: " << result << std::endl;
    return 0;
}
//使用 std::packaged_task (包在线程函数外)------------------------------------------
#include <iostream>
#include <future>
#include <thread>
int compute() {
    std::this_thread::sleep_for(std::chrono::seconds(2));
    return 42;
}
int main() {
    // 创建 packaged_task 
    std::packaged_task<int()> task(compute);
 编程客栈   // 获取 future
    std::future<int> fut = task.get_future();
    // 在另一个线程中执行任务
    std::thread t(std::move(task));
    t.join();
    // 获取结果
    int result = fut.get();
    std::cout << "Result: " << result << std::endl;
    return 0;
}
// 使用 std::promise (作为线程函数参数) -----------------------------------------------
#include <iostream>
#include <future>
#include <thread>
void compute(std::promise<int> prom) {
    std::this_thread::sleep_for(std::chrono::seconds(2));
    prom.set_value(42);
}
int main() {
    // 创建 promise 和 future
    std::promise<int> prom;
    std::future<int> fut = prom.get_future();
    // 在另一个线程中执行任务
    std::thread t(compute, std::move(prom));
    // 获取结果
    int result = fut.get();
    std::cout << "Result: " << result << std::endl;
    t.join();
    return 0;
}

异步机制总结: C++11 的异步机制(std::future、std::async、std::packaged_task 和 std::promise)相比传统的多线程编程,提供了以下额外的好处:

更高的抽象层次,简化了异步操作的管理。

自动化的结果传递和异常处理。

更灵活的线程管理和任务执行策略。

更清晰的代码结构和更低的耦合度。

支持任务组合和超时等待。

这些机制使异步编程更加直观、安全和高效,是现代 C++ 并发编程的重要组成部分。

总结

C++ 多线程编程提供了强大的工具来处理并发任务。通过使用 std::threadstd::mutexstd::condition_variable 等工具,你可以编写高效且安全的多线程程序。需要注意的是,多线程编程容易引入数据竞争和死锁等问题,因此需要仔细设计和测试。

到此这篇关于c++ 标准库多线程的文章就介绍到这了,更多相关c++ 标准库多线程内容请搜索编程客栈(www.devze.com)以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程客栈(www.devze.com)!

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号