本文介绍了在项目开发过程中的thread问题以及高精度定时器POSIX的使用出现的问题
1. 高精度计时器 POSIX 的使用
一般的应用:
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
| #include "MaxDataWorker.h" #include "max30102.h" #include <iostream> #include <chrono> #include <thread> #include <signal.h>
using namespace std; using namespace chrono;
int datacount = 0;
void timer_handler(int sig, siginfo_t *si, void *uc) { MAX30102 *data = (MAX30102 *)si->si_value.sival_ptr; data->get_data(); datacount++; }
MaxDataWorker::MaxDataWorker(QObject *parent, MAX30102 *sensor) : QObject(parent), max30102(sensor) { }
MaxDataWorker::~MaxDataWorker() { }
void MaxDataWorker::doWork() { struct sigevent sev; struct sigaction sa; sa.sa_flags = SA_SIGINFO; sa.sa_sigaction = timer_handler; sigemptyset(&sa.sa_mask); sigaction(SIGRTMIN, &sa, NULL);
sev.sigev_notify = SIGEV_SIGNAL; sev.sigev_signo = SIGRTMIN; sev.sigev_value.sival_ptr = max30102;
timer_t timerid; if (timer_create(CLOCK_REALTIME, &sev, &timerid) == -1) { perror("timer_create"); }
struct itimerspec its; its.it_value.tv_sec = 0; its.it_value.tv_nsec = 12500000; its.it_interval.tv_sec = 0; its.it_interval.tv_nsec = 12500000;
if (timer_settime(timerid, 0, &its, NULL) == -1) { perror("timer_settime"); }
std::this_thread::sleep_for(std::chrono::seconds(5)); timer_delete(timerid); cout << "datacount is :" << datacount << endl; emit finishRead(); }
|
需要注意的是:
sa.sa_sigaction = timer_handler;
:只能使用普通函数或静态成员函数。
- 如果需要访问类成员函数,需通过
sigev_value.sival_ptr
传递对象指针:
1 2 3 4 5 6
| void timer_handler(int sig, siginfo_t *si, void *uc) { MAX30102 *data = (MAX30102 *)si->si_value.sival_ptr; data->get_data(); datacount++; }
|
1
| sev.sigev_value.sival_ptr = max30102;
|
POSIX 定时器本身就是一个线程。
2. 关于线程 QThread 和 std::thread
可参考:
C++ 与 Qt 线程:C++ std::thread 与 Qt QThread 多线程混合编程
3. QThread 与 POSIX 的使用
- QThread 与 POSIX 均可用信号机制,不会冲突。
- 使用
pthread
启动 POSIX 定时器时,可能引发信号干扰,影响其他 QThread 的信号处理。
解决方案:在 MaxPlot.cpp
全局使用 POSIX 定时器,它本身就是线程,可定时触发数据读取逻辑。
4. 多线程竞争与原子操作问题
当定时器频繁触发同一方法,若前一次调用未完成,可能导致竞态问题。
可使用 std::atomic
原子操作避免冲突:
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
| void MaxPlot::posix_timer_handler(union sigval sv) { MAX30102 *data = static_cast<MAX30102 *>(sv.sival_ptr);
bool expected = false;
if (data->is_reading.compare_exchange_strong(expected, true)) { data->get_data(); data->datacount++;
if (data->datacount >= 360) { std::cout << "data is up to full" << std::endl; data->datacount = 0; emit data->finishRead(); data->Quit(); cout << "Jump is:" << data->count_Jump << endl; data->count_Jump = 0; }
data->is_reading = false; } else { std::cerr << "上一次读取未完成,跳过此次读取" << std::endl; data->count_Jump++; data->datacount++; } }
|