C++ 多线程相关
C++ 多线程相关
std::thread
/*******************************************************************************************
std::thread
如果没有调用 join 或者 detach 函数,假如线程函数执行时间较长,此时线程对象的生命周期结束调用析构函数清理资源,
这时可能会发生错误,这里有两种解决办法,一个是调用 join(),保证线程函数的生命周期和线程对象的生命周期相同,
另一个是调用 detach(),将线程和线程对象分离,
这里需要注意,如果线程已经和对象分离,那我们就再也无法控制线程什么时候结束了,不能再通过 join 来等待线程执行完。
*******************************************************************************************/
#include <iostream>
#include <thread>
using namespace std;
int main() {
auto func1 = []() {
for (int i = 0; i < 10; i++) {
cout << i << " ";
}
cout << "当前线程ID " << std::this_thread::get_id() << endl;
};
std::thread t1(func1);
if (t1.joinable()) {
t1.detach();
}
auto func2 = [](int k) {
for (int i = 0; i < k; i++) {
cout << i << " ";
}
cout << endl;
};
std::thread t2(func2, 20);
if (t2.joinable()) {
t2.join();
}
// c++11 还提供了获取线程 id,或者系统 cpu 个数
cout << "当前 CPU 个数 " << std::thread::hardware_concurrency() << endl;
// 获取 thread native_handle,使得线程休眠等功能 handle 可用于 pthread 相关操作
auto handle = t1.native_handle();
std::this_thread::sleep_for(std::chrono::seconds(1));
return 0;
}
// 编译:g++ thread_commen.cpp -std=c++11 -lpthread
/*****************************************************************
std::thread
对 thread 进行封装,避免没有调用 join 或者 detach 可导致程序出错的情况出现
*******************************************************************/
#include <iostream>
#include <thread>
#include <chrono>
using namespace std;
class ThreadGuard {
public:
enum class DesAction { join, detach };
ThreadGuard(std::thread&& t, DesAction a) :
t_(std::move(t)), action_(a) {}
~ThreadGuard() {
if (t_.joinable()) {
if (action_ == DesAction::join) {
t_.join();
} else {
t_.detach();
}
}
}
ThreadGuard(ThreadGuard&&) = default;
ThreadGuard& operator=(ThreadGuard&&) = default;
std::thread& get() { return t_; }
private:
std::thread t_;
DesAction action_;
};
int main() {
ThreadGuard t(std::thread([]() {
for (int i = 0; i < 10; i++) {
cout << "thread guard " << i << " ";
}
cout << endl;
}), ThreadGuard::DesAction::join);
return 0;
}
std::mutex 和 std::timed_mutex
/***********************************************************
std::mutex 是一种线程同步的手段,用于保存多线程同时操作的共享数据。
* std::mutex 分为四种:
* 1 std::mutex:独占的互斥量,不能递归使用,不带超时功能
* 2 std::recursive_mutex:递归互斥量,可重入,不带超时功能
* 3 std::timed_mutex:带超时的互斥量,不能递归
* 4 std::recursive_timed_mutex:带超时的互斥量,可以递归使用
***********************************************************/
#include <iostream>
#include <mutex>
#include <thread>
using namespace std;
std::mutex mutex_;
int main() {
auto func1 = [](int k) {
mutex_.lock();
for (int i = 0; i < k; ++i) {
cout << i << " ";
}
cout << endl;
mutex_.unlock();
};
std::thread threads[5];
for (int i = 0; i < 5; ++i) {
threads[i] = std::thread(func1, 200);
}
for (auto& th : threads) {
if (th.joinable()) {
th.join();
}
}
return 0;
}
/* std::timed_mutex:*/
#include <iostream>
#include <mutex>
#include <thread>
#include <chrono>
using namespace std;
std::timed_mutex timed_mutex_;
int main() {
auto func1 = [](int k) {
timed_mutex_.try_lock_for(std::chrono::milliseconds(200));
for (int i = 0; i < k; ++i) {
cout << i << " ";
}
cout << endl;
timed_mutex_.unlock();
};
std::thread threads[5];
for (int i = 0; i < 5; ++i) {
threads[i] = std::thread(func1, 200);
}
for (auto& th : threads) {
if (th.joinable()) {
th.join();
}
}
return 0;
}
std::lock_guard 和 std::unique_lock
/* std::lock_guard 和 std::unique_lock
* std::lock_gurad 相比于 std::unique_lock 更加轻量级,少了一些成员函数,std::unique_lock 类有 unlock 函数,可以手动释放锁,
* 所以条件变量都配合 std::unique_lock 使用,而不是 std::lock_guard,因为条件变量在 wait 时需要有手动释放锁的能力
*/
#include <iostream>
#include <mutex>
#include <thread>
using namespace std;
std::mutex mutex_;
int main() {
auto func1 = [](int k) {
// std::lock_guard<std::mutex> lock(mutex_);
std::unique_lock<std::mutex> lock(mutex_);
for (int i = 0; i < k; ++i) {
cout << i << " ";
}
cout << endl;
};
std::thread threads[5];
for (int i = 0; i < 5; ++i) {
threads[i] = std::thread(func1, 200);
}
for (auto& th : threads) {
if (th.joinable()) {
th.join();
}
}
return 0;
}
std::atomic
/*****************************
std::atomic<T> 原子变量
******************************/
//计数器
#include <iostream>
#include <atomic>
#include <thread>
#include <mutex>
using namespace std;
// 普通计数器
struct OriginCounter {
int count = 0;
std::mutex mutex_;
void add() {
std::lock_guard<std::mutex> lock(mutex_);
++count;
}
void sub() {
std::lock_guard<std::mutex> lock(mutex_);
--count;
}
int get() {
std::lock_guard<std::mutex> lock(mutex_);
return count;
}
};
// 普通计数器
struct NewCounter {
std::atomic<int> count{0};
void add() {
++count;
// count.store(++count); 这种方式也可以
}
void sub() {
--count;
// count.store(--count); 这种方式也可以
}
int get() {
return count.load();
}
};
int main() {
OriginCounter originCounter;
NewCounter newCounter;
// 创建多个线程来增加和减少计数
std::thread t1([&]() {
for (int i = 0; i < 10000; ++i) {
originCounter.add();
newCounter.add();
}
});
std::thread t2([&]() {
for (int i = 0; i < 10000; ++i) {
originCounter.sub();
newCounter.sub();
}
});
// 等待线程结束
t1.join();
t2.join();
// 获取结果
cout << "originCounter: " << originCounter.get() << endl;
cout << "newCounter: " << newCounter.get() << endl;
return 0;
}
/* 输出结果:
* originCounter: 0
* newCounter: 0
*/
std::call_once
/****************************************************************
C++11 提供 std::call_once 用来保证某一函数在多线程环境下只被调用一次
std::call_once 需要配合 std::once_flag 使用
******************************************************************/
#include <iostream>
#include <thread>
#include <mutex> // std::call_once
using namespace std;
std::once_flag g_onceflag;
void call_once_test() {
std::call_once(g_onceflag, []() {
cout << "call_once_test" << endl;
});
}
int main() {
std::thread threads[5];
for (int i = 0; i < 5; ++i) {
threads[i] = std::thread(call_once_test);
}
for (auto& th : threads) {
th.join();
}
return 0;
}
std::condition_varible
/****************************************************************
C++11 提供 std::condition_varible 线程同步方式
std::condition_varible 是一个能够阻止调用线程的对象,直到通知继续。
它是实现生产者-消费者问题(避免轮询,事件通知)和其他需要线程同步的情况时的重要工具
condition_variable类型的对象总是使用unique_lock等待
1.wait() :阻塞线程
2.notify_one() :通知一个线程的wait(),唤醒线程去尝试拿锁。拿锁失败返回false,成功则执行任务
3.notify_all() :通知所有线程
******************************************************************/
#include <iostream>
#include <deque>
#include <thread>
#include <mutex>
#include <condition_variable>
using namespace std;
std::deque<int> g_deque;
std::mutex g_mutex;
std::condition_variable g_cond;
void thread_producer() {
int count = 10;
while (count > 0) {
std::unique_lock<std::mutex> lock(g_mutex);
g_deque.push_front(count);
lock.unlock();
cout << "producer a value: " << count << endl;
g_cond.notify_one();
std::this_thread::sleep_for(std::chrono::seconds(1));
count--;
}
}
void thread_consumer() {
int data = 0;
while (data != 1) {
std::unique_lock<std::mutex> lock(g_mutex);
while (g_deque.empty()) {
g_cond.wait(lock);
}
data = g_deque.back();
g_deque.pop_back();
cout << "consumer a value: " << data << endl;
lock.unlock();
}
}
int main() {
std::thread t1(thread_consumer);
std::thread t2(thread_producer);
t1.join();
t2.join();
return 0;
}
/* 运行结果:
* producer a value: 10
* consumer a value: 10
* producer a value: 9
* consumer a value: 9
* producer a value: 8
* consumer a value: 8
* producer a value: 7
* consumer a value: 7
* producer a value: 6
* consumer a value: 6
* producer a value: 5
* consumer a value: 5
* producer a value: 4
* consumer a value: 4
* producer a value: 3
* consumer a value: 3
* producer a value: 2
* consumer a value: 2
* producer a value: 1
* consumer a value: 1
*/
std::thread_local
c++11 引入 thread_local,用 thread_local 修饰的变量具有 thread 周期,每一个线程都拥有并只拥有一个该变量的独立实例,一般用于需要保证线程安全的函数中。
对于一个线程私有变量,一个线程拥有且只拥有一个该实例,类似于 static。
/*****************************
std::thread_local
******************************/
#include <iostream>
#include <thread>
using namespace std;
class A {
public:
A() {}
~A() {}
void test(const string& name) {
thread_local int count = 0;
++count;
cout << name << ": " << count << endl;
}
};
void func(const string& name) {
A a1;
a1.test(name);
a1.test(name);
A a2;
a2.test(name);
a2.test(name);
}
int main() {
std::thread(func, "thread1").join();
std::thread(func, "thread1").join();
return 0;
}