C++多线程学习笔记-1
进程与线程:
进程是运行中的程序。
线程是进程中可并行的一个子程序,主要目的是提高程序运行效率。最大线程个数是CPU核数。
Thread线程库的基本使用
1 |
|
定义一个线程:
1
std::thread(function_name, args)
等待线程完成
1
t.join()
等待线程完成后主程序才往下运行。如果没有这一句,上面的线程在没有打印完msg时主程序已结束,就会报错。
分离线程
1
t.detach()
分离线程,让它在后台运行。往往用于多进程的情况。
joinable
1
bool isJoin = t.joinable();
joinable()方法返回一个布尔值,如果线程可以被join()或detach(),则返回true,否则返回false。防止直接使用join报错。
变量未定义问题
传递的函数需要引用,指针类型变量时容易出现的线程没运行完,变量已被释放的问题。
函数参数是引用
1
2
3
4
5
6
7
8
9
10
11
12void foo(int& x){
x += 1;
}
int main()
{
int a = 1;
std::thread t(foo, std::ref(a));
t.join();
std::cout << a << endl;
return 0;
}std::ref用于取某个变量的引用,但要保证在线程函数执行期间,变量
a
的生命周期是有效的。函数参数是指针
1
2
3
4
5
6
7
8
9
10
11
12void foo(int* x){
std::cout << *x << std::endl;
}
int main()
{
int *ptr = new int(1);
std::thread t(foo, ptr);
// delete ptr;
t.join();
return 0;
}如果在注释处释放指针,线程还没执行完,foo函数根据地址取到的就是一个未知的数。
解决方法:用智能指针
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15class A{
public:
void foo(){
std::cout<< "Hello!" << std::endl;
}
};
int main()
{
std::shared_ptr<A> a = std::make_shared<A>;
std::thread t(&A::foo, a);
t.join();
return 0;
}用智能指针来定义需要传递的指针参数,这样该参数就能在不需要的时候自动析构。
入口函数为类的私有函数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21class A{
private:
// friend void thread_foo();
void foo(){
std::cout<< "Hello!" << std::endl;
}
};
void thread_foo()
{
std::shared_ptr<A> a = std::make_shared<A>;
std::thread t(&A::foo, a);
t.join();
}
int main()
{
thread_foo();
return 0;
}如果直接运行,会报错,因为函数thread_foo无法访问类的私由函数。但用注释那行,将该函数设为友元,则可以访问。
互斥量mutex
多个线程访问一个变量,且对变量进行修改,此时会出现函数竞争问题,导致函数运行结果不确定。因此需要用到互斥锁,使得每次只能有一个程序访问变量。
1 |
|
倘若不加互斥锁,可能导致多个线程对a操作,此时操作重叠就可能出现问题。加了互斥锁,每次只能有一个线程访问a,使得最终a=20000
死锁
如下面程序中的情况,线程t1
运行func1
,获取了mtx1
的所有权,如果此时正好线程t2
运行func2
获得了mtx2
的所有权。那就会出现t1
,t2
都在等对方,都无法运行的死锁。
1 |
|
参考资料
C++多线程学习笔记-1
https://sisyphus-99.github.io/2023/08/25/多线程学习笔记-1/