0%

boost库使用记录

一、前言

1. cmake使用

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
# 指定使用boost库的静态库还是动态库
set(Boost_USE_STATIC_LIBS ON)

# 1.70: 指定boost库最低版本1.70,可以不加
# REQUIRED: 必须找到,不然报错
# COMPONENTS: 找到filesystem和system两个模块,模块路径会加到Boost_LIBRARIES变量中,不加模块此变量为空
# 不添加模块查找,Boost_LIBRARY_DIRS同样也为空
find_package(Boost 1.70 REQUIRED
COMPONENTS filesystem system
)

# 头文件必须加,否则会找不到
target_include_directories(${BIN_NAME} PRIVATE
# 第三方库头文件
${Boost_INCLUDE_DIR}
)

# 两种方式添加,一种是指定要链接的库
target_link_libraries(
${BIN_NAME} PRIVATE
# 第三方库
${Boost_LIBRARIES}
)
# 另一种是指定库所在目录,在编译时自动添加
target_link_directories(
${BIN_NAME} PRIVATE
# 第三方库
${Boost_LIBRARY_DIRS}
)

二、使用实例

boost/algorithm/string/predicate.hpp

1. 忽略大小写对比字符串

1
bool ret = boost::iequals(str1, str2);

boost/asio.hpp

1. asio::io_context 任务队列

  • post放到队列最后,等待调度
  • dispatch如果在当前线程,就直接执行,不在就和post一致
  • 整个队列实测是顺序执行的
  • stop()调用后需要调用restart()才能重新进行run()

示例1 单线程模型

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
37
38
39
#include <boost/asio.hpp>

#include "log.hpp"

using namespace std;

void testFunc() {
boost::asio::io_context ioc;
boost::asio::io_context::work worker(ioc);

std::thread t1([&ioc]() {
ioc.run();
LOGI(WHAT("ioc exit"));
});
// detach用于让线程自由,防止函数退出时出现栈溢出
t1.detach();
// worker就是在这种情况下起作用,这时一直没有任务,ioc不退出
std::this_thread::sleep_for(std::chrono::seconds(3));

std::promise<void> p;
int testValue = 0;
LOGI(WHAT("push task"));
// 添加一个任务到ioc中
ioc.post([&testValue, &p, &ioc]() {
std::this_thread::sleep_for(std::chrono::seconds(3));
testValue = 1;
p.set_value();
});
p.get_future().wait();
LOGI(WHAT("testValue {}", testValue));
}

int main(int argC, char *argV[]) {
testFunc();
LOGI(WHAT("main end"));
// 这里等待用于查看worker作用域消失,ioc会退出
std::this_thread::sleep_for(std::chrono::seconds(5));
return 0;
}

输出

1
2
3
4
[2022-05-30 15:57:17.166] [info] [main.cpp:37] push task
[2022-05-30 15:57:20.166] [info] [main.cpp:44] testValue 1
[2022-05-30 15:57:20.167] [info] [main.cpp:30] ioc exit
[2022-05-30 15:57:20.167] [info] [main.cpp:74] main end

解释一下

  • 使用worker防止线程退出,所以线程detach后没有打印ioc exit,而函数退出后,worker作用域没了,ioc就退出了
  • 如果不使用worker,ioc一开始就会退出,程序将卡死在p.get_future().wait()

示例2 多线程模型

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
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
using namespace std;

class Test {
public:
Test(int num) {
cout << "Test()" << endl;
for (int i = 0; i < num; i++) {
m_futures.emplace_back(async(launch::async, [this]() {
try {
// 几个线程调用run,任务就会分发到几个线程
boost::asio::io_context::work work(m_ioContext);
m_ioContext.run();
} catch (...) {
std::stringstream ss;
ss << boost::stacktrace::stacktrace();
auto error = boost::current_exception_diagnostic_information();
auto stack = ss.str();
LOGE(WHAT("thread exit with exception"), REASON("error {}, stack \n{}", error, stack),
WILL("thread exit"));
}
}));
}
}
~Test() {
cout << "~Test()" << endl;
// 要先停止ioContext才能等待future,否则会因为work存在卡死
m_ioContext.stop();
for (auto& f : m_futures) {
if (f.valid()) f.wait();
}
}

void post(std::function<void()> func) {
m_ioContext.post(func);
}

private:
vector<future<void>> m_futures;
boost::asio::io_context m_ioContext;
};

int main(int argC, char *argV[]) {
Test test(3);

LOGI(WHAT("push task"));
std::vector<std::promise<void>> promises(100);
for (size_t i = 0; i < 100; i++) {
auto &p = promises[i];
auto taskFunc = [i, &p]() {
std::this_thread::sleep_for(std::chrono::milliseconds((100 - i) * 10));
LOGI(WHAT("task {} done", i));
p.set_value();
};
test.post(taskFunc);
}
LOGI(WHAT("push task done"));

for (size_t i = 0; i < promises.size(); i++) {
promises[i].get_future().get();
}
LOGI(WHAT("taskDone"));
return 0;
}

2. asio::ip::tcp::resolver dns解析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
int main(int argC, char *argV[]) {
boost::asio::io_context ioc;
boost::asio::io_context::work worker(ioc);
// 要新起一个线程进行处理dns请求
auto asyncFuture = std::async(std::launch::async, [&ioc]() {
ioc.run();
});

boost::asio::ip::tcp::resolver resolver(ioc);
boost::asio::ip::tcp::resolver::query query("www.baidu.com", "http");

// 这里解析是同步的
auto result = resolver.resolve(query);
for (auto it = result.begin(); it != result.end(); ++it) {
LOGI(WHAT("resolve success! domain={}, host_name={}, service_name={}, address={}", "www.baidu.com",
it->host_name(), it->service_name(), it->endpoint().address().to_string()));
}
ioc.stop();
return 0;
}

3. boost::asio::steady_timer 定时器

  • 定时器需要依赖ioContext进行初始化
  • 没有提供周期性的定时器,需要每次定时器超时重新设置定时器超时时间
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
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
using IOContext = boost::asio::io_context;
using IOContextPtr = std::shared_ptr<IOContext>;

class Test {
private:
IOContextPtr m_pIOContext; // 全局唯一工作线程
std::future<void> m_future; // 工作线程的future
boost::asio::steady_timer m_timer; // 定时器,用于清理ctx
int m_timeout; // 定时器周期,单位ms

public:
explicit Test(int timeout) : m_pIOContext(std::make_shared<IOContext>()), m_timer(*m_pIOContext), m_timeout(timeout) {
// 启动工作线程
m_future = std::async(std::launch::async, [this]() {
try {
boost::asio::io_context::work work(*m_pIOContext);
m_pIOContext->run();
} catch (...) {
std::stringstream ss;
ss << boost::stacktrace::stacktrace();
auto error = boost::current_exception_diagnostic_information();
auto stack = ss.str();
LOGE(WHAT("thread exit with exception"), REASON("error {}, stack \n{}", error, stack),
WILL("thread exit"));
}
});

// 启动定时器
setTimer();
}

~Test() { stop(); }

void stop() {
// 停止线程,循环防止ioContext还没跑就停止造成死锁
do {
if (m_pIOContext != nullptr && !m_pIOContext->stopped()) {
m_pIOContext->stop();
}
// 等待工作线程结束
if (!m_future.valid()) {
break;
}
} while (m_future.wait_for(std::chrono::milliseconds(1)) != std::future_status::ready);
}

private:
void setTimer() {
m_timer.expires_after(std::chrono::milliseconds(m_timeout));
// async_wait不会阻塞,可以认为是启用定时器
m_timer.async_wait([this](const boost::system::error_code &ec) {
if (ec) {
// 错误存在两种情况,ioContext停止不会触发错误
// 1. 定时器还在等待,重新调用expires_after
// 2. 显式调用cancel
LOGE(WHAT("timer error"), REASON("ec: {}", std::to_string(ec)), NO_WILL);
return;
}
// do something
// 重新设置定时器
LOGI(WHAT("Timer handler"));
setTimer();
});
}
};

boost/compute/detail/lru_cache.hpp

1. 源码

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
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
// boost/compute/detail/lru_cache.hpp
template<class Key, class Value>
class lru_cache
{
public:
typedef Key key_type;
typedef Value value_type;
typedef std::list<key_type> list_type;
typedef std::map<
key_type,
std::pair<value_type, typename list_type::iterator>
> map_type;

lru_cache(size_t capacity)
: m_capacity(capacity)
{
}

~lru_cache()
{
}

size_t size() const
{
return m_map.size();
}

size_t capacity() const
{
return m_capacity;
}

bool empty() const
{
return m_map.empty();
}

bool contains(const key_type &key)
{
return m_map.find(key) != m_map.end();
}

void insert(const key_type &key, const value_type &value)
{
typename map_type::iterator i = m_map.find(key);
if(i == m_map.end()){
// insert item into the cache, but first check if it is full
if(size() >= m_capacity){
// cache is full, evict the least recently used item
evict();
}

// insert the new item
m_list.push_front(key);
m_map[key] = std::make_pair(value, m_list.begin());
}
}

boost::optional<value_type> get(const key_type &key)
{
// lookup value in the cache
typename map_type::iterator i = m_map.find(key);
if(i == m_map.end()){
// value not in cache
return boost::none;
}

// return the value, but first update its place in the most
// recently used list
typename list_type::iterator j = i->second.second;
if(j != m_list.begin()){
// move item to the front of the most recently used list
m_list.erase(j);
m_list.push_front(key);

// update iterator in map
j = m_list.begin();
const value_type &value = i->second.first;
m_map[key] = std::make_pair(value, j);

// return the value
return value;
}
else {
// the item is already at the front of the most recently
// used list so just return it
return i->second.first;
}
}

void clear()
{
m_map.clear();
m_list.clear();
}

private:
void evict()
{
// evict item from the end of most recently used list
typename list_type::iterator i = --m_list.end();
m_map.erase(*i);
m_list.erase(i);
}

private:
map_type m_map;
list_type m_list;
size_t m_capacity;
};

2. 使用说明

  • insert只能首次插入key,已存在的key调用insert会没有任何效果
  • get返回的不是value,而是boost::optional,需要使用其再次调用get才能返回value
    • get不存在的key将返回boost::none,如果对其调用get会直接抛异常
  • get会将对应的key放到最前面,但get出来的值是一个右值,修改不会影响lru中的value
  • contains不会影响顺序

boost/filesystem/path.hpp

1. 路径字符串操作

1
2
3
4
5
6
7
8
#include <boost/filesystem/path.hpp>

int main(int argC, char *argV[]) {
std::string test = "C:\\ww\\asdf asdf\\aaa.txt";
boost::filesystem::path p(test);
LOG_INFO("{}", p.filename().string()); // aaa.txt
return 0;
}

boost/format.hpp

1. 拼接变量到字符串

1
2
3
4
5
6
7
8
9
10
11
12
#include <boost/format.hpp>

static std::string to_string(const RunLoopCB& cb) {
const char *jsonFormat = R"({"fileName":"%1%","callThead":"%2%","calleeName":"%3%","funcName":"%4%","calleeLineNum":%5%})";
boost::format fmt = boost::format(jsonFormat)
% cb.fileName
% cb.callTid
% cb.calleeName
% cb.funcName
% cb.calleeLineNum;
return fmt.str();
}

boost/multi_index_container.hpp

1. 多重索引表

  • 可以看作是数据库在内存中的映射,可以堆一个数据结构建立多个不同的索引,查找时更加方便
  • 暂时没找到主次排序字段怎么定义,只能使用主排序
  • 关键key也可以使用自定义类型,但是要实现>=<=><==!=这些运算符
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
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
#include <boost/multi_index/key_extractors.hpp>     // boost::multi_index::member
#include <boost/multi_index/ordered_index.hpp> // boost::multi_index::ordered_unique 和 boost::multi_index::ordered_non_unique
#include <boost/multi_index_container.hpp>

struct Student {
int id;
std::string name;
int age;
};

namespace std {
string to_string(const Student &stu) {
stringstream ss;
ss << "{";
ss << R"("id":)" << stu.id;
ss << R"(,"name":")" << stu.name << "\"";
ss << R"(,"age":")" << stu.age;
ss << "}";
return ss.str();
}
} // namespace std

// 下面的定义是为了获取tag,类似map的key
struct tagID {};
struct tagName {};
struct tagAge {};

using StudentMap = boost::multi_index_container<
Student,
boost::multi_index::indexed_by<
// 学号为唯一key
boost::multi_index::ordered_unique<boost::multi_index::tag<tagID>,
boost::multi_index::member<Student, int, &Student::id>>,
// 名称可以不唯一,但是要有序
boost::multi_index::ordered_non_unique<boost::multi_index::tag<tagName>,
boost::multi_index::member<Student, std::string, &Student::name>>,
// 年龄同上
boost::multi_index::ordered_non_unique<boost::multi_index::tag<tagAge>,
boost::multi_index::member<Student, int, &Student::age>>>>;

int main(int argc, char *argv[]) {
// 获取名称排序
auto &studentsName = students.get<tagName>(); // 这里必须用引用
// 查找,根据名称查找,但是不确定查到的是两个同名的哪一个
auto iterName = studentsName.find("bbd");
if (iterName != studentsName.end()) {
LOGI(WHAT("find student {}", std::to_string(*iterName)));
}
// 查找名叫bbd的下界,第一个等于bbd的
iterName = studentsName.lower_bound("bbd");
// 查找名叫bbd的上界,不等于bbd
auto iterNameEnd = studentsName.upper_bound("bbd");
// 遍历输出所有bbd学生信息
while (iterName != iterNameEnd) {
LOGI(WHAT("bbd student {}", std::to_string(*iterName)));
++iterName;
}
// 删除
iterName = studentsName.find("bbd");
if (iterName != studentsName.end()) {
LOGI(WHAT("delete student {}", std::to_string(*iterName)));
studentsName.erase(iterName);
}
// 遍历
for (auto iter = studentsName.begin(); iter != studentsName.end(); ++iter) {
LOGI(WHAT("{}", std::to_string(*iter)));
}

// 修改map,学生bbd改名字了,需要先找到学生的迭代器才能修改
iterName = studentsName.find("bbd");
// modify的回调是在更新索引前调用的,根据回调修改的内容进行更新索引
ret = studentsName.modify(iterName, [](Student &stu) { stu.name = "cbd"; }); // 更改成功
// 如果modify改动把主索引重复了,就会删除当前修改的迭代器,更改会失败
// 更新学生学号和zbd一样,当前更新信息的学生被删除
auto stu = *iterName;
ret = studentsName.modify(iterName, [](Student &stu) { stu.id = 3; }); // 更改失败
LOGI(WHAT("update student {} ret {}", std::to_string(stu), ret));
for (auto iter = studentsName.begin(); iter != studentsName.end(); ++iter) {
LOGI(WHAT("{}", std::to_string(*iter)));
}

return 0;
}

boost/stacktrace.hpp

1. 打印堆栈信息

1
2
3
4
5
6
7
8
9
10
11
12
13
m_future = std::async(std::launch::async, [this]() {
try {
boost::asio::io_context::work work(*m_pIOContext);
m_pIOContext->run();
} catch (...) {
std::stringstream ss;
ss << boost::stacktrace::stacktrace();
auto error = boost::current_exception_diagnostic_information();
auto stack = ss.str();
LOGE(WHAT("thread exit with exception"), REASON("error {}, stack \n{}", error, stack),
WILL("thread exit"));
}
});

效果

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
2022-08-16 10:49:46 [237723:237724][E][main.cpp:71 operator()] thread exit with exception; Reason: error Dynamic exception type: int, stack
0# 0x000055B4979DD01C in build/output/run
1# 0x000055B4979F81D8 in build/output/run
2# 0x000055B4979F819B in build/output/run
3# 0x000055B4979F813A in build/output/run
4# 0x000055B4979F80CC in build/output/run
5# 0x000055B4979F7DAC in build/output/run
6# 0x000055B4979F77A3 in build/output/run
7# 0x000055B4979F7101 in build/output/run
8# 0x000055B4979F65D6 in build/output/run
9# 0x000055B4979DFD9C in build/output/run
10# 0x000055B4979DB1B8 in build/output/run
11# 0x000055B4979E6D27 in build/output/run
12# 0x000055B4979E319F in build/output/run
13# 0x000055B4979DFB56 in build/output/run
14# 0x000055B4979E31CB in build/output/run
15# 0x000055B4979E31DC in build/output/run
16# 0x00007F36189BC717 in /usr/lib/libc.so.6
17# 0x000055B4979D032A in build/output/run
18# 0x000055B4979DFBD7 in build/output/run
19# 0x000055B4979DAF97 in build/output/run
20# 0x000055B4979F3FC8 in build/output/run
21# 0x000055B4979FA812 in build/output/run
22# 0x000055B4979FA497 in build/output/run
23# 0x000055B4979FA167 in build/output/run
24# 0x000055B4979F9AC2 in build/output/run
25# 0x000055B4979F855E in build/output/run
26# 0x00007F3618CD62F3 in /usr/lib/libstdc++.so.6
27# 0x00007F36189B778D in /usr/lib/libc.so.6
28# __clone in /usr/lib/libc.so.6
; Will: thread exit

2. 打印带函数和行号的堆栈信息

  • 需要在cmake里面添加backtrace的链接库和BOOST_STACKTRACE_USE_BACKTRACE的宏
  • 需要系统中存在libbacktrace.so,不存在就百度找办法安装
1
2
3
4
5
6
7
target_link_libraries(
${BIN_NAME} PRIVATE
backtrace
)
target_compile_definitions(${BIN_NAME} PRIVATE
BOOST_STACKTRACE_USE_BACKTRACE
)

效果

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
2022-08-16 10:52:29 [242804:242805][E][main.cpp:71 operator()] thread exit with exception; Reason: error Dynamic exception type: int, stack
0# Test::Test()::{lambda()#1}::operator()() const at /home/test/src/app/main.cpp:68
1# void std::__invoke_impl<void, Test::Test()::{lambda()#1}>(std::__invoke_other, Test::Test()::{lambda()#1}&&) in build/output/run
2# std::__invoke_result<Test::Test()::{lambda()#1}>::type std::__invoke<Test::Test()::{lambda()#1}>(Test::Test()::{lambda()#1}&&) in build/output/run
3# void std::thread::_Invoker<std::tuple<Test::Test()::{lambda()#1}> >::_M_invoke<0ul>(std::_Index_tuple<0ul>) in build/output/run
4# std::thread::_Invoker<std::tuple<Test::Test()::{lambda()#1}> >::operator()() in build/output/run
5# std::__future_base::_Task_setter<std::unique_ptr<std::__future_base::_Result<void>, std::__future_base::_Result_base::_Deleter>, std::thread::_Invoker<std::tuple<Test::Test()::{lambda()#1}> >, void>::operator()() const in build/output/run
6# std::unique_ptr<std::__future_base::_Result<void>, std::__future_base::_Result_base::_Deleter> std::__invoke_impl<std::unique_ptr<std::__future_base::_Result<void>, std::__future_base::_Result_base::_Deleter>, std::__future_base::_Task_setter<std::unique_ptr<std::__future_base::_Result<void>, std::__future_base::_Result_base::_Deleter>, std::thread::_Invoker<std::tuple<Test::Test()::{lambda()#1}> >, void>&>(std::__invoke_other, std::__future_base::_Task_setter<std::unique_ptr<std::__future_base::_Result<void>, std::__future_base::_Result_base::_Deleter>, std::thread::_Invoker<std::tuple<Test::Test()::{lambda()#1}> >, void>&) in build/output/run
7# std::enable_if<is_invocable_r_v<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter>, std::__future_base::_Task_setter<std::unique_ptr<std::__future_base::_Result<void>, std::__future_base::_Result_base::_Deleter>, std::thread::_Invoker<std::tuple<Test::Test()::{lambda()#1}> >, void>&>, std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> >::type std::__invoke_r<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter>, std::__future_base::_Task_setter<std::unique_ptr<std::__future_base::_Result<void>, std::__future_base::_Result_base::_Deleter>, std::thread::_Invoker<std::tuple<Test::Test()::{lambda()#1}> >, void>&>(std::__future_base::_Task_setter<std::unique_ptr<std::__future_base::_Result<void>, std::__future_base::_Result_base::_Deleter>, std::thread::_Invoker<std::tuple<Test::Test()::{lambda()#1}> >, void>&) in build/output/run
8# std::_Function_handler<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> (), std::__future_base::_Task_setter<std::unique_ptr<std::__future_base::_Result<void>, std::__future_base::_Result_base::_Deleter>, std::thread::_Invoker<std::tuple<Test::Test()::{lambda()#1}> >, void> >::_M_invoke(std::_Any_data const&) in build/output/run
9# std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>::operator()() const in build/output/run
10# std::__future_base::_State_baseV2::_M_do_set(std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*) in build/output/run
11# void std::__invoke_impl<void, void (std::__future_base::_State_baseV2::*)(std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*), std::__future_base::_State_baseV2*, std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*>(std::__invoke_memfun_deref, void (std::__future_base::_State_baseV2::*&&)(std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*), std::__future_base::_State_baseV2*&&, std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*&&, bool*&&) at /usr/include/c++/12.1.1/bits/invoke.h:74
12# std::__invoke_result<void (std::__future_base::_State_baseV2::*)(std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*), std::__future_base::_State_baseV2*, std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*>::type std::__invoke<void (std::__future_base::_State_baseV2::*)(std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*), std::__future_base::_State_baseV2*, std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*>(void (std::__future_base::_State_baseV2::*&&)(std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*), std::__future_base::_State_baseV2*&&, std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*&&, bool*&&) in build/output/run
13# std::call_once<void (std::__future_base::_State_baseV2::*)(std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*), std::__future_base::_State_baseV2*, std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*>(std::once_flag&, void (std::__future_base::_State_baseV2::*&&)(std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*), std::__future_base::_State_baseV2*&&, std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*&&, bool*&&)::{lambda()#1}::operator()() const in build/output/run
14# std::once_flag::_Prepare_execution::_Prepare_execution<std::call_once<void (std::__future_base::_State_baseV2::*)(std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*), std::__future_base::_State_baseV2*, std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*>(std::once_flag&, void (std::__future_base::_State_baseV2::*&&)(std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*), std::__future_base::_State_baseV2*&&, std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*&&, bool*&&)::{lambda()#1}>(void (std::__future_base::_State_baseV2::*&)(std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*))::{lambda()#1}::operator()() const in build/output/run
15# std::once_flag::_Prepare_execution::_Prepare_execution<std::call_once<void (std::__future_base::_State_baseV2::*)(std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*), std::__future_base::_State_baseV2*, std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*>(std::once_flag&, void (std::__future_base::_State_baseV2::*&&)(std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*), std::__future_base::_State_baseV2*&&, std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*&&, bool*&&)::{lambda()#1}>(void (std::__future_base::_State_baseV2::*&)(std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*))::{lambda()#1}::_FUN() in build/output/run
16# 0x00007FF52BDBC717 in /usr/lib/libc.so.6
17# __gthread_once(int*, void (*)()) at /usr/include/c++/12.1.1/x86_64-pc-linux-gnu/bits/gthr-default.h:700
18# void std::call_once<void (std::__future_base::_State_baseV2::*)(std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*), std::__future_base::_State_baseV2*, std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*>(std::once_flag&, void (std::__future_base::_State_baseV2::*&&)(std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*), std::__future_base::_State_baseV2*&&, std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*&&, bool*&&) in build/output/run
19# std::__future_base::_State_baseV2::_M_set_result(std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>, bool) in build/output/run
20# std::__future_base::_Async_state_impl<std::thread::_Invoker<std::tuple<Test::Test()::{lambda()#1}> >, void>::_M_run() in build/output/run
21# void std::__invoke_impl<void, void (std::__future_base::_Async_state_impl<std::thread::_Invoker<std::tuple<Test::Test()::{lambda()#1}> >, void>::*)(), std::__future_base::_Async_state_impl<std::thread::_Invoker<std::tuple<Test::Test()::{lambda()#1}> >, void>*>(std::__invoke_memfun_deref, void (std::__future_base::_Async_state_impl<std::thread::_Invoker<std::tuple<Test::Test()::{lambda()#1}> >, void>::*&&)(), std::__future_base::_Async_state_impl<std::thread::_Invoker<std::tuple<Test::Test()::{lambda()#1}> >, void>*&&) at /usr/include/c++/12.1.1/bits/invoke.h:74
22# std::__invoke_result<void (std::__future_base::_Async_state_impl<std::thread::_Invoker<std::tuple<Test::Test()::{lambda()#1}> >, void>::*)(), std::__future_base::_Async_state_impl<std::thread::_Invoker<std::tuple<Test::Test()::{lambda()#1}> >, void>*>::type std::__invoke<void (std::__future_base::_Async_state_impl<std::thread::_Invoker<std::tuple<Test::Test()::{lambda()#1}> >, void>::*)(), std::__future_base::_Async_state_impl<std::thread::_Invoker<std::tuple<Test::Test()::{lambda()#1}> >, void>*>(void (std::__future_base::_Async_state_impl<std::thread::_Invoker<std::tuple<Test::Test()::{lambda()#1}> >, void>::*&&)(), std::__future_base::_Async_state_impl<std::thread::_Invoker<std::tuple<Test::Test()::{lambda()#1}> >, void>*&&) at /usr/include/c++/12.1.1/bits/invoke.h:97
23# void std::thread::_Invoker<std::tuple<void (std::__future_base::_Async_state_impl<std::thread::_Invoker<std::tuple<Test::Test()::{lambda()#1}> >, void>::*)(), std::__future_base::_Async_state_impl<std::thread::_Invoker<std::tuple<Test::Test()::{lambda()#1}> >, void>*> >::_M_invoke<0ul, 1ul>(std::_Index_tuple<0ul, 1ul>) at /usr/include/c++/12.1.1/bits/std_thread.h:252
24# std::thread::_Invoker<std::tuple<void (std::__future_base::_Async_state_impl<std::thread::_Invoker<std::tuple<Test::Test()::{lambda()#1}> >, void>::*)(), std::__future_base::_Async_state_impl<std::thread::_Invoker<std::tuple<Test::Test()::{lambda()#1}> >, void>*> >::operator()() at /usr/include/c++/12.1.1/bits/std_thread.h:259
25# std::thread::_State_impl<std::thread::_Invoker<std::tuple<void (std::__future_base::_Async_state_impl<std::thread::_Invoker<std::tuple<Test::Test()::{lambda()#1}> >, void>::*)(), std::__future_base::_Async_state_impl<std::thread::_Invoker<std::tuple<Test::Test()::{lambda()#1}> >, void>*> > >::_M_run() in build/output/run
26# execute_native_thread_routine at /usr/src/debug/gcc/libstdc++-v3/src/c++11/thread.cc:84
27# 0x00007FF52BDB778D in /usr/lib/libc.so.6
28# __clone in /usr/lib/libc.so.6
; Will: thread exit

boost/thread/pthread/thread_data.hpp

1. 和系统时间无关的线程休眠

1
boost::this_thread::sleep_for(boost::chrono::seconds(5));

小技巧和踩坑记

1. 下面接口不带ec会抛异常

1
boost::asio::steady_timer::cancel();