C++多线程学习笔记-3

获取线程返回值

我们希望执行线程后获得线程执行结果的返回值。

解决方法:

  1. 直接在线程函数参数中加入作为返回值的引用或指针参数
  2. 使用std::future和std::promise

std::future,它表示存储着一个未来会被初始化的变量。这个变量可以通过std::future提供的成员函数std::future::get()来得到。如果在这个变量被赋值之前就有别的线程试图通过std::future::get()获取这个变量,那么这个线程将会被阻塞到这个变量可以获取为止。

std::promise同样也是一个类模板,这个对象承诺在未来一定会初始化一个变量(这个变量也就是std::future中的变量)。

每一个std::promise对象都有一个与之关联的std::future对象。当std::promise设置值的时候,这个值就会赋给std::future中的对象了。

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
#include <vector>
#include <thread>
#include <future>
#include <numeric>
#include <iostream>
#include <chrono>

// 函数计算vector的和,并需要返回sum
void accumulate(std::vector<int>::iterator first,
std::vector<int>::iterator last,
std::promise<int> accumulate_promise)
{
int sum = std::accumulate(first, last, 0);
accumulate_promise.set_value(sum); // Notify future
}

int main()
{
// 用promise<int>来在线程之间传递数据
std::vector<int> numbers = { 1, 2, 3, 4, 5, 6 };
std::promise<int> accumulate_promise;
std::future<int> accumulate_future = accumulate_promise.get_future();
std::thread work_thread(accumulate, numbers.begin(), numbers.end(),
std::move(accumulate_promise));

std::cout << "result=" << accumulate_future.get() << '\n'; // get()函数会一直阻塞直到accumulate_promise被赋予了有效值
work_thread.join();
}

用这种方式相比指针或引用的好处在于不需要等待此线程运行完了才返回值,一旦有了需要的值,另一个线程就可以得到信号并获取值。这种功能本来需要设置信号条件变量来实现。

cout输出乱码

当多个线程都有cout输出时,控制台上输出内容可能会交错混乱,这是因为cout不是线程安全的.

1
cout << "a=" << a << endl;

它把传入的数据依次存入缓冲区。这中间是有可能有其他线程插入的。

解决方法:

  1. 使用printf代替cout
    1
    printf("a=%d\n",a);
  2. 加锁
1
2
unique_lock<mutex> lock(mtx);
cout << "a=" << a << endl;

多个线程存入vector

希望程序根据输入决定调用线程的数量,此时用一个vector存储线程

1
2
3
4
5
6
7
8
9
10
11
std::vector<std::thread> threads;
for (int i = 0; i < num_thread; i++)
{
threads.push_back(std::thread(&Calibrator::RandomSearch, this, int(search_count / num_thread), xyz_range, rpy_range, i));
}
for (int i = 0; i < num_thread; i++)
{
if(threads[i].joinable()){
threads[i].join();
}
}

此时不能先声明thread,再把它push_back,否则就会报错

1
error: use of deleted function 'std::thread::thread(const std::thread&)'

这是因为vector::push_back会先拷贝再推入,而thread是禁止拷贝构造的。可以如上面的直接构造一个临时变量,推入vector.或者用std::move把左值引用转为右值引用。

左值是表达式结束后依然存在的持久对象(代表一个在内存中占有确定位置的对象),能够取到地址

右值是表达式结束时不再存在的临时对象(不在内存中占有确定位置的表达式)

多线程随机数生成重复

如果只在主线程里用srand,那么其他子线程随机数一定是相同的。生成随机数的每个线程都需要重新设置时间种子。

但如果用时间做随机种子

1
srand((unsigned int)time(NULL));
由于时间以秒为单位,如果多个线程调用时间接近,还是可能随机种子相同导致随机数重复。

解决方法:

把线程编号传入,使用(时间+编号)生成随机种子。


C++多线程学习笔记-3
https://sisyphus-99.github.io/2023/09/14/多线程学习笔记-3/
Author
sisyphus
Posted on
September 14, 2023
Licensed under