文章目录
-
- [0. 概述](#0. 概述)
- [1. 解析std::thread()线程创建失败问题](#1. 解析std::thread()线程创建失败问题)
-
- [1.1 问题背景](#1.1 问题背景)
- [1.2 问题分析](#1.2 问题分析)
- [1.3 解决方案](#1.3 解决方案)
- [2. 使用示例代码](#2. 使用示例代码)
- [3. 自定义线程管理解决方案](#3. 自定义线程管理解决方案)
-
- [3.1 自定义线程类设计](#3.1 自定义线程类设计)
- [3.2 自定义线程类实现](#3.2 自定义线程类实现)
0. 概述
为了解决std::thread()线程创建失败的问题,我们常常遇到系统资源紧张导致pthread_create()函数返回EAGAIN错误,进而可能抛出std::system_error异常,如"Resource temporarily unavailable"或类似的错误信息。
定位过程请看:定位和分析解决std::thread创建失败的问题和解决方法(mmap虚拟地址耗尽)
为了解决这一问题,本文引入了一个自定义的线程管理类:MyThread。通过这个自定义类,我们能够精确控制线程的创建、销毁、优先级调度、CPU亲和性设置以及等待唤醒机制。
MyThread类基于C++11标准,专为QNX和Linux平台设计,且代码符合Misra标准。它提供了以下主要功能:
- 线程的创建与销毁管理
- 线程的优先级调度
- 线程的CPU亲和性设置
- 线程的等待与唤醒机制
1. 解析std::thread()线程创建失败问题
1.1 问题背景
在使用std::thread创建线程时,常见的问题是由于系统资源不足或其他限制条件导致的线程创建失败。这种情况通常会抛出std::system_error异常,如"Resource temporarily unavailable"或类似的错误信息。
1.2 问题分析
为了准确识别和解决std::thread()线程创建失败的问题,需要深入分析其底层实现和系统调用机制。std::thread通常依赖于pthread_create()函数来创建新线程。当系统资源紧张时,pthread_create()可能会返回EAGAIN错误,表示资源暂时不可用。
1.3 解决方案
为了提高线程创建的成功率和系统稳定性,我们推荐采用自定义的线程管理类。这种类能够根据特定平台的需求进行优化,并提供更精细化的线程管理能力,从而避免std::thread可能引发的异常情况。
2. 使用示例代码
以下是一个简单的测试代码,展示了如何使用自定义的MyThread类来管理多线程:
cpp
#include "my_thread.h"
#include <chrono>
#include <iostream>
#include <string>
namespace MyTest {
static void worker_function() {
std::this_thread::sleep_for(std::chrono::milliseconds(100));
std::cout << "Thread ID: " << std::this_thread::get_id() << " is working." << std::endl;
}
} // namespace MyTest
int32_t main() {
int32_t main_res{0};
try {
const uint32_t num_threads = 4;
cpu_set_t cpuset1;
CPU_ZERO(&cpuset1);
CPU_SET(0, &cpuset1);
std::vector<std::shared_ptr<MyTest::MyThread>> test_threads;
for (uint32_t thread_idx = 0; thread_idx < num_threads; ++thread_idx) {
const std::string thread_name = std::string("Thread_") + std::to_string(thread_idx);
const std::shared_ptr<MyTest::MyThread> my_thread = std::make_shared<MyTest::MyThread>(
thread_name, 1, 1, MyTest::worker_function, sizeof(cpuset1), &cpuset1);
test_threads.push_back(my_thread);
}
for (const auto& my_thread : test_threads) {
my_thread->thread_start();
}
std::this_thread::sleep_for(std::chrono::seconds(2));
for (const auto& my_thread : test_threads) {
my_thread->thread_shutdown();
}
for (const auto& my_thread : test_threads) {
while (!my_thread->has_shutdown()) {
my_thread->timed_wait(100);
}
}
std::cout << "All threads have been shutdown." << std::endl;
} catch (...) {
std::cerr << "Exception occurred" << std::endl;
main_res = 1;
}
return main_res;
}
3. 自定义线程管理解决方案
3.1 自定义线程类设计
为了在Linux-ARM和QNX等嵌入式平台上避免std::thread可能引发的线程创建异常,我们设计了一个自定义的MyThread类。该类具有以下设计特点:
- 可移植性: 兼容多种嵌入式平台,减少对特定平台的依赖性。
- 资源管理: 精确控制线程资源的分配和释放,避免系统资源不足的问题。
- 效率: 优化的线程管理策略,提高了线程的创建和销毁效率,降低了系统开销。
3.2 自定义线程类实现
以下是MyThread类的关键实现代码,展示了如何通过封装pthread库和标准C++11特性来实现高效的线程管理:
头文件 my_thread.h
cpp
#ifndef UTILS_MY_THREAD_H_
#define UTILS_MY_THREAD_H_
#include <atomic>
#include <cstdint>
#include <functional>
#include <mutex>
#include <string>
#include <thread>
#include <vector>
#include <condition_variable>
#include <memory>
namespace MyTest {
class MyThread {
public:
explicit MyThread(const std::string& name, int32_t priority, uint32_t worker_num,
const std::function<void()>& func, int32_t cpusetsize, const cpu_set_t* cpuset);
~MyThread();
void thread_start();
void thread_shutdown();
bool has_shutdown() const {
return is_shutdown_;
}
void timed_wait(uint32_t useconds);
static void set_self_thread_priority(int32_t priority);
private:
static void thread_function(MyThread* thread_instance);
private:
std::string thread_name_;
int32_t thread_priority_;
uint32_t thread_worker_num_;
std::vector<std::shared_ptr<std::thread>> threads_;
std::function<void()> main_function_;
int32_t thread_cpusetsize_;
const cpu_set_t* thread_cpuset_ = nullptr;
std::mutex thread_mutex_;
std::condition_variable thread_cond_;
bool is_shutdown_;
};
} // namespace MyTest
#endif // UTILS_MY_THREAD_H_
实现文件 my_thread.cpp
cpp
#include "my_thread.h"
#include <sstream>
#include <sys/types.h>
#include "fake_log.h"
namespace MyTest {
MyThread::MyThread(const std::string& name, int32_t priority, uint32_t worker_num,
const std::function<void()>& func, int32_t cpusetsize, const cpu_set_t* cpuset)
: thread_name_(name),
thread_priority_(priority),
thread_worker_num_(worker_num),
main_function_(func),
thread_cpusetsize_(cpusetsize),
thread_cpuset_(cpuset),
is_shutdown_(true) {
}
MyThread::~MyThread() {
if (!is_shutdown_) {
thread_shutdown();
}
}
void MyThread::thread_function(MyThread* thread_instance) {
if (thread_instance == nullptr) {
MY_LOG_ERROR("thread_instance is nullptr");
return;
}
if (thread_instance->thread_cpuset_ != nullptr && thread_instance->thread_cpusetsize_ > 0) {
const pthread_t tid = pthread_self();
const auto np_return = pthread_setaffinity_np(tid, static_cast<size_t>(thread_instance->thread_cpusetsize_), thread_instance->thread_cpuset_);
if (np_return != 0) {
MY_LOG_ERROR("pthread_setaffinity_np failed. return=%d", np_return);
}
}
std::stringstream thread_id_stream;
thread_id_stream << std::this_thread::get_id();
MY_LOG_INFO("Thread %s starts. pid=%s target_priority=%d", thread_instance->thread_name_.c_str(),
thread_id_stream.str().c_str(), thread_instance->thread_priority_);
MyThread::set_self_thread_priority(thread_instance->thread_priority_);
try {
thread_instance->main_function_();
} catch (...) {
MY_LOG_ERROR("Exception occurred in thread %s", thread_instance->thread_name_.c_str());
}
}
void MyThread::thread_start() {
std::lock
_guard<std::mutex> lock(thread_mutex_);
is_shutdown_ = false;
threads_.resize(thread_worker_num_);
for (uint32_t idx = 0; idx < thread_worker_num_; ++idx) {
threads_[idx] = std::make_shared<std::thread>(thread_function, this);
}
}
void MyThread::thread_shutdown() {
std::lock_guard<std::mutex> lock(thread_mutex_);
if (!is_shutdown_) {
is_shutdown_ = true;
thread_cond_.notify_all();
for (const auto& thread : threads_) {
if (thread->joinable()) {
thread->join();
}
}
}
}
void MyThread::timed_wait(uint32_t useconds) {
std::unique_lock<std::mutex> lock(thread_mutex_);
const auto timeout_val = std::chrono::microseconds(useconds);
do {
const auto return_val = thread_cond_.wait_for(lock, timeout_val);
if (return_val == std::cv_status::timeout) {
MY_LOG_ERROR("Thread timed_wait timeout");
}
} while (false);
}
void MyThread::set_self_thread_priority(int32_t priority) {
bool go_on = true;
struct sched_param params;
struct sched_param current_params;
int32_t set_policy{0};
int32_t current_policy{0};
const pthread_t this_thread = pthread_self();
int32_t status_ret = pthread_getschedparam(this_thread, ¤t_policy, ¤t_params);
if (status_ret != 0) {
MY_LOG_ERROR("getschedparam %d", status_ret);
go_on = false;
} else {
MY_LOG_DEBUG("Thread current priority is %d (%d), target is %d", current_params.sched_priority, current_policy,
priority);
if (priority == 0) {
go_on = false;
} else if (priority > 0) {
set_policy = SCHED_FIFO;
params.sched_priority = current_params.sched_priority + priority;
} else {
set_policy = SCHED_IDLE;
params.sched_priority = 0;
}
}
if (go_on) {
if (params.sched_priority > 99) {
params.sched_priority = 99;
}
if (params.sched_priority < 0) {
params.sched_priority = 0;
}
status_ret = pthread_setschedparam(this_thread, set_policy, ¶ms);
if (status_ret != 0) {
MY_LOG_WARN("setschedparam(%d)", params.sched_priority);
go_on = false;
}
}
if (go_on) {
status_ret = pthread_getschedparam(this_thread, ¤t_policy, ¤t_params);
if (status_ret != 0) {
MY_LOG_ERROR("getschedparam 2 %d", status_ret);
} else {
if (current_params.sched_priority != params.sched_priority) {
MY_LOG_ERROR("Current priority=%d (%d), target is %d", current_params.sched_priority, current_policy,
params.sched_priority);
} else {
MY_LOG_INFO("Set thread priority to %d (%d)", current_params.sched_priority, current_policy);
}
}
}
}
} // namespace MyTest