0%

并发编程

记录学习并发编程学到的东西。

1.仿函数作为参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class back
{
public:
void operator()()
{
cout << "hello " << endl;
}
};
//thread t(back());
//t.join();这里会当作名为t的函数,返回值类型为thread,back()是函数指针
//如果给线程传递仿函数并带有参数std::thread t2(background_task(), str);这样也能正常使用
thread t((back()));
t.join();


2.detach()和局部变量可能引发的问题
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
struct func {
/*
使用引用类型作为类成员变量时,可以通过构造函数的初始化列表来初始化它。
这是因为引用必须在创建时绑定到一个对象,并且不能在以后更改。
*/
func(int& i) :_i(i) {}//引用的参数用参数列表来初始化
void operator()()
{
for (size_t i = 0; i < 3; i++)
{
_i = i;
cout << "this _i is " << _i << endl;
}
}
int& _i;
};

void oops()
{
/*
这个函数是有问题的,把some_locate_state作为一个引用去访问它的地址
但是,因为是detach所以分开进行,如果主线程先结束了,那么局部变量就会被释放,
它所在的地址就为空了,就会导致悬空引用。
使用 std::shared_ptr 可以避免这种问题,因为它通过引用计数来管理对象的生命周期,
只要有一个shared_ptr持有对象,该对象就不会被销毁。
*/
int some_locate_state = 0;
func myfunc(some_locate_state);
thread mythread(myfunc);
mythread.detach();

/*
* shared_ptr记录有多少个指针指向同一个同一个变量,当引用计数为0时,变量才会被释放
* 所以说是线程安全的
*/
}

3.异常捕捉需要把子线程先执行完

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
void catch_exception()
{
//主线程做一些事可能导致线程崩溃,在主线程回收资源之前要把子线程先进行完
int some_local_state = 0;
func myfunc(some_local_state);
thread mythread(myfunc);
try
{
std::this_thread::sleep_for(std::chrono::seconds(1));
}
catch (const std::exception& e)
{
mythread.join();
throw;
}

mythread.join();
}


全部逻辑都写在try_catch里面太臃肿,封装了一个thread_guard类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
class thread_guard//这是一个单例类
{
private:
thread& _t;
public:
//explicit 关键字禁止隐式转换,防止单例模式意外创建对象
explicit thread_guard(thread& t) :_t(t){}
~thread_guard()
{
//join只调用一次
if (_t.joinable())
{
_t.join();
}
}
thread_guard(const thread_guard&) = delete;
thread_guard& operator=(thread_guard&) = delete;
};

void auto_guard()
{
int some_local_state = 0;
func myfunc(some_local_state);
thread t(myfunc);
thread_guard g(t);
}

4.彻底搞懂const char 和 char const 的区别
一言以蔽之:”const char p指针指向的内容是常量,指针可以修改;char const p指针是常量,指针指向的内容可以修改。”
char* const p

1
2
3
4
5
char data[] = "Hello";
char* const ptr = data;

ptr[0] = 'h'; // 正确:可以修改指针指向的内容
// ptr = nullptr; // 错误:不能修改指针本身

const char* p

1
2
3
4
const char* str = "Hello";

// str[0] = 'h'; // 错误:不能修改指针指向的内容
str = "World"; // 正确:可以修改指针本身

5.extern关键字的作用
最常用:声明全局变量
在多文件项目中,如果需要在一个文件中使用另一个文件中定义的全局变量,可以使用 extern 关键字。extern 告诉编译器该变量在其他地方定义,链接器将负责解析其定义位置。
在多文件项目中,可以在一个头文件中用extern声明全局变量,在另一个源文件中定义该变量。这确保了变量只有一个定义但是可以在多个文件中使用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
//file 1
#include <iostream>

int global_variable = 42; // 定义全局变量

void print_global_variable() {
std::cout << "Global variable: " << global_variable << std::endl;
}

//file 2
#include <iostream>

extern int global_variable; // 声明全局变量

void modify_global_variable() {
global_variable = 100;
}

//main
void print_global_variable();
void modify_global_variable();

int main() {
print_global_variable(); // 输出: Global variable: 42
modify_global_variable();
print_global_variable(); // 输出: Global variable: 100
return 0;
}