C++ 多线程
- 头文件
#include <thread>
创建线程 thread
jion与detach方式的区别
- jion方式:必须等待已经创建的子线程任务执行完毕,才会继续往下执行。
示例:
void my_func1_task()
{
cout << "I am my_func1_task" << endl;
cout << "my_func1_task end" << endl;
}
void my_func2_task(int x)
{
cout << "I am my_func2_task" << endl;
while (x--)
{
cout << "x:" << x << ";";
sleep(1);
}
cout << endl;
cout << "my_func2_task end" << endl;
}
int main(int argc, char *argv[])
{
cout << "this is main()" << endl;
thread thread_1(my_func1_task);
//等待线程1完成
thread_1.join();
thread thread_2(my_func2_task, 10);
//等待线程2完成
thread_2.join();
cout << "thread end" << endl;
cout << "main end" << endl;
return 0;
}
测试结果:
- detach方式:启动的子线程自主在后台运行,当前主线程代码继续往下执行,无需等待子线程结束。
注:该种情况下,主线程一般会进入while(1)循环执行主线程逻辑,否则整个进程会结束。
示例:
void my_func1_task()
{
cout << "I am my_func1_task" << endl;
cout << "my_func1_task end" << endl;
}
void my_func2_task(int x)
{
cout << "I am my_func2_task" << endl;
while (x--)
{
cout << "x:" << x << ";";
sleep(1);
}
cout << endl;
cout << "my_func2_task end" << endl;
}
int main(int argc, char *argv[])
{
cout << "this is main()" << endl;
thread thread_1(my_func1_task);
thread_1.detach();
thread thread_2(my_func2_task, 10);
thread_2.detach();
cout << "thread end" << endl;
cout << "main end" << endl;
return 0;
}
测试结果:
互斥锁 mutex
- 头文件
#include <mutex>
lock和unlock
不上锁测试示例:
int g_count = 10;
void my_func1_task()
{
while (g_count-- > 0)
{
cout << "===func1===";
sleep(1);
}
cout << endl;
}
void my_func2_task()
{
while (g_count-- > 0)
{
cout << "***func2***";
sleep(1);
}
cout << endl;
}
int main(int argc, char *argv[])
{
cout << "this is main()" << endl;
thread thread_1(my_func1_task);
thread thread_2(my_func2_task);
thread_1.join();
thread_2.join();
return 0;
}
测试结果:
上锁测试示例:
int g_count = 10;
mutex g_countMtx;
void my_func1_task()
{
g_countMtx.lock();
while (g_count-- > 0)
{
cout << "===func1===";
sleep(1);
}
cout << endl;
g_countMtx.unlock();
}
void my_func2_task()
{
g_countMtx.lock();
while (g_count-- > 0)
{
cout << "***func2***";
sleep(1);
}
cout << endl;
g_countMtx.unlock();
}
int main(int argc, char *argv[])
{
cout << "this is main()" << endl;
thread thread_1(my_func1_task);
thread thread_2(my_func2_task);
thread_1.join();
thread_2.join();
return 0;
}
测试结果:
lock_guard
- 作用:防止在lock后,忘记unlock。
- 使用:在创建lock_guard对象时,将尝试获取提供给它的互斥锁的所有权。当控制流离开lock_guard对象的作用域时,lock_guard析构并释放互斥锁。
- 特点:创建即加锁,作用域结束自动析构并解锁;不能中途解锁,必须等作用域结束才能解锁。
- 使用技巧:可以根据需要通过使用{}增加临时作用域。
测试示例-不加锁
int g_count = 0;
void func_1_task()
{
while (1) {
cout << "-------------------------" << endl;
cout << "--- set count: " << ++g_count << endl;
this_thread::sleep_for(chrono::seconds(1));
}
}
void func_2_task()
{
while (1) {
cout << "*************************" << endl;
cout << "***111 get count: " << g_count << endl;
this_thread::sleep_for(chrono::seconds(1));
cout << "***222 get count: " << g_count << endl;
this_thread::sleep_for(chrono::seconds(1));
}
}
int main()
{
thread th_1(func_1_task);
th_1.detach();
thread th_2(func_2_task);
th_2.detach();
while (1)
{
this_thread::sleep_for(chrono::seconds(10));
}
return 0;
}
测试结果
测试示例-加锁
int g_count = 0;
mutex g_countMtx;
void func_1_task()
{
while (1) {
{
lock_guard<mutex> guard(g_countMtx);
cout << "-------------------------" << endl;
cout << "--- set count: " << ++g_count << endl;
}
this_thread::sleep_for(chrono::seconds(1));
}
}
void func_2_task()
{
while (1) {
{
lock_guard<mutex> guard(g_countMtx);
cout << "*************************" << endl;
cout << "***111 get count: " << g_count << endl;
this_thread::sleep_for(chrono::seconds(1));
cout << "***222 get count: " << g_count << endl;
}
this_thread::sleep_for(chrono::seconds(1));
}
}
int main()
{
thread th_1(func_1_task);
th_1.detach();
thread th_2(func_2_task);
th_2.detach();
while (1)
{
this_thread::sleep_for(chrono::seconds(10));
}
return 0;
}
测试结果
信号量 sem
C++下使用的信号量,实际上使用的也是C语言中的信号量,即使用<semaphore.h>库。
sem参数
- 头文件
#include <semaphore.h>
- 类型
sem_t - 函数接口
int sem_init(sem_t *sem, int pshared, unsigned int value); // 创建信号量
int sem_post(sem_t *sem); // 信号量的值加 1
int sem_wait(sem_t *sem); // 信号量的值减 1
int sem_destroy(sem_t *sem); // 信号量销毁
示例
sem_t sem;
void func_1_task()
{
cout << "start wait sem..." << endl;
sem_wait(&sem);
cout << "end wait sem..." << endl;
}
void func_2_task()
{
sleep(5);
cout << "I will post sem" << endl;
sem_post(&sem);
}
int main()
{
sem_init(&sem, 0, 0);
thread th_1(func_1_task);
th_1.detach();
thread th_2(func_2_task);
th_2.detach();
while (1)
{
sleep(10);
}
sem_destroy(&sem);
return 0;
}
测试结果
原子变量atomic
- 多线程编程中常用的同步机制,它能确保对共享变量的操作在执行时不被其它线程的操作干扰。
- 保证操作是原子级别的,要么全部完成,要么全部未完成。
- 原子类型:整形、指针、布尔值。语法:std::atomic< T >
- 内存模型:通过指定memory orders实现线程安全
- 多线程安全,效率比互斥量高
成员函数
is_lock_free()函数
- 检查当前atomic对象是否支持无锁操作。
store()函数
- 将给定的值存储到原子对象中。
- 存储操作的内存顺序默认为:std::memory_order_seq_cst(顺序一致性)
load()函数
- 获取原子变量的当前值。
- 内存操作默认为:std::memory_order_seq_cst(顺序一致性)
exchange()函数
- 替换并返回原来的值。
支持的基本操作总结
- 加法:a += n 或 a.fetch_add(n)
- 减法:a -= n 或 a.fetch_sub(n))
- 自增:a++ 或 a.fetch_add(1)
- 自减:a-- 或 a.fetch_sub(1)
- 与:a &= b 或 a.fetch_add(b)
- 或:a |= b 或 a.fetch_or(b)
- 异或:a ^= b 或 a.fetch_xor(b)
- 交换:a.exchange(b)
原子变量应用示例
多线程不加锁
static int g_total = 0;
void func_task()
{
int i = 0;
for (i = 1; i < 101; i++) {
g_total += i;
this_thread::sleep_for(chrono::milliseconds(100));
}
}
int main()
{
thread th_1(func_task);
thread th_2(func_task);
th_1.join();
th_2.join();
cout << "*** g_total: " << g_total << endl;
return 0;
}
测试结果
多线程加锁
static int g_total = 0;
mutex mtx;
void func_task()
{
int i = 0;
for (i = 1; i < 101; i++) {
{
lock_guard<mutex> guard(mtx);
g_total += i;
this_thread::sleep_for(chrono::milliseconds(100));
}
}
}
int main()
{
thread th_1(func_task);
thread th_2(func_task);
th_1.join();
th_2.join();
cout << "*** g_total: " << g_total << endl;
return 0;
}
测试结果
多线程使用原子变量
//static int g_total = 0;
atomic<int> g_total(0);
void func_task()
{
int i = 0;
for (i = 1; i < 101; i++) {
g_total += i;//或 total.fetch_add(i);
this_thread::sleep_for(chrono::milliseconds(100));
}
}
int main()
{
thread th_1(func_task);
thread th_2(func_task);
th_1.join();
th_2.join();
cout << "*** g_total: " << g_total.load() << endl;
return 0;
}
测试结果