目录
(2)不一致状态(Inconsistent State)的例子
2. 锁管理器(std::lock_guard 和 std::unique_lock)
(补充)关于emplace_back与 push_back 的区别
基本概念
-
线程:线程是程序执行的最小单位,一个进程可以包含多个线程。
-
并发:多个线程同时执行,但不一定是真正的同时(取决于CPU核心数)。
-
并行:多个线程真正同时执行(需要多核CPU)。
C++中的多线程
C++11引入了std::thread
类,用于创建和管理线程。你可以通过传递一个函数或可调用对象来创建一个线程。
示例1:创建线程并执行函数
#include <iostream>
#include <thread>
using namespace std;
void hello() {
cout << "Hello from thread!" << endl;
}
int main() {
//创建一个线程t,并执行hello函数。
thread t(hello);
//主线程main等待t线程执行完毕。
//如果不调用join(),主线程可能会在子线程完成之前结束,导致未定义行为
t.join(); // 等待线程结束
cout << "Hello from main!" << endl;
return 0;
}
示例2:传递参数给线程函数
#include <iostream>
#include<string>
#include <thread>
using namespace std;
void hello(string str) {
cout << str << endl;
}
int main() {
string s="hello form s!";
thread t(hello,s);
t.join(); // 等待线程结束
cout << "Hello from main!" << endl;
return 0;
}
示例3:使用Lambda表达式
#include <iostream>
#include <thread>
using namespace std;
int main() {
//使用Lambda表达式作为线程函数,简洁且方便。
thread t([](){
cout<<"hello from lambda thread!"<<endl;
});
t.join(); // 等待线程结束
cout << "Hello from main!" << endl;
return 0;
}
线程同步
线程同步是多线程编程中非常重要的概念,尤其是在多个线程需要共享资源(如变量、数据结构等)时。如果没有正确的同步机制,可能会导致数据竞争(Data Race)或未定义行为(Undefined Behavior)
什么是线程同步?
线程同步是指通过某种机制,确保多个线程在访问共享资源时能够有序、安全地进行操作,避免数据竞争和不一致的问题。
为什么需要线程同步?
在多线程环境中,多个线程可能同时访问和修改共享资源。如果没有同步机制,可能会导致以下问题:
-
数据竞争:多个线程同时修改同一个变量,导致结果不可预测。
-
不一致状态:一个线程正在修改数据,而另一个线程读取了未完全更新的数据,导致逻辑错误。
(1)数据竞争(Data Race)的例子
-
定义:多个线程同时访问共享资源(至少有一个线程是写操作),且没有正确的同步机制。
-
关注点:内存访问的冲突,即多个线程同时读写同一块内存。
-
表现:程序行为不可预测,可能导致崩溃、数据损坏或结果错误。
示例代码:
#include <iostream>
#include <thread>
using namespace std;
int shared_value = 0; // 共享变量
void increment(char name) {
//多个线程同时修改 shared_value
for (int i = 0; i < 100000; ++i) {
shared_value++;
if(i==99999) cout<<"over\n";
}
}
int main() {
thread a(increment,'a');
thread b(increment,'b');
a.join();
b.join();
cout << "Final value: " << shared_value << endl;
return 0;
}
结果:
一定输出两个over, 但是Final value每次运行的结果可能不同,例如 :
Final value: 166278
Final value: 132402
问题分析:
首先,两个线程都输出了over,说明它们确确实实地执行了方法,方法中完成了100000次循环。
那么问题就只能出现在shared_value的自增操作上。
-
shared_value++
并不是一个原子操作,它分为以下几步:-
从内存中读取
shared_value
的值。 -
将值加 1。
-
将结果写回内存。
-
-
由于没有同步机制,多个线程可能同时读取和修改
shared_value
,可能会导致其中一个线程的修改被覆盖。 -
表现:最终结果可能小于预期值(
200000
),且每次运行结果可能不同。
(2)不一致状态(Inconsistent State)的例子
-
定义:多个线程同时访问共享资源,导致共享资源处于逻辑上不一致的状态。
-
关注点:逻辑正确性,即共享资源的语义是否被破坏。
-
表现:共享资源的逻辑状态不符合预期,但程序可能不会崩溃。
示例代码:
#include <iostream>
#include <thread>
using namespace std;
struct BankAccount {//银行账户结构体
int balance = 0;
void deposit(int amount) {
balance += amount; // 存款
}
void withdraw(int amount) {
balance -= amount; // 取款
}
};
BankAccount account; // 共享的银行账户
void deposit_money() {
for (int i = 0; i < 100000; ++i) {
account.deposit(1); // 每次存 1 元
if(i==99999) cout<<"存款完毕\n";
}
}
void withdraw_money() {
for (int i = 0; i < 100000; ++i) {
account.withdraw(1); // 每次取 1 元
if(i==99999) cout<<"取款完毕\n";
}
}
int main() {
thread t1(deposit_money);
thread t2(withdraw_money);
t1.join();
t2.join();
cout <<"Final balance: "<<account.balance <<endl;
return 0;
}
结果:
一定会输出存款完毕和取款完毕,但是Final balance每次运行的结果都不同,比如:
Final balance: 29467
Final balance: -38703
问题分析
-
balance += amount
和balance -= amount
都不是原子操作。 -
如果两个线程同时修改
balance
,可能会导致以下情况:-
线程 A 读取
balance
的值为 100。 -
线程 B 读取
balance
的值也为 100。 -
线程 A 将
balance
更新为 101。 -
线程 B 将
balance
更新为 99。
-
-
表现:最终
balance
的值可能是错误的,甚至出现负数(如-123
),但程序不会崩溃。