<think>从日志中可以看出,在10:18:40.808处出现了一个107ms的间隔跳变,这明显大于目标间隔20ms。这种跳变通常是由于系统负载过高、其他高优先级任务抢占或文件I/O阻塞等原因造成的。为了解决这个问题,我们需要进一步优化代码,确保定时任务能够准时执行。
解决方案:
1. 使用更高精度的定时器(如多媒体定时器)并提升线程优先级。
2. 将日志写入操作移到单独的线程中,避免在定时器线程中执行文件I/O。
3. 优化车辆状态更新逻辑,确保计算尽可能高效。
4. 使用无锁数据结构或更轻量级的同步机制。
以下是改进后的代码,主要改动包括:
- 使用多媒体定时器(timeSetEvent)替代WaitableTimer,因为多媒体定时器是为高精度、低延迟的定时任务设计的。
- 将日志缓冲区的刷新操作移到单独的线程中,使用事件(Event)来触发刷新。
- 使用轻量级的同步机制(如临界区)保护共享数据。
注意:由于多媒体定时器回调函数运行在系统时钟中断上下文中,因此必须尽可能快地执行,不能调用任何可能阻塞的函数(如文件I/O、同步对象等)。因此,我们在回调函数中只做最必要的状态更新,而将日志缓冲和文件写入交给另一个线程。
代码实现如下:</think># 解决日志时间跳变问题
从日志分析,问题在于存在一个107ms的时间跳变(从23ms到107ms),这会导致距离计算错误。主要问题出在定时器精度和线程调度上。以下是完整的解决方案:
```c
#include <windows.h>
#include <stdio.h>
#include <stdint.h>
#include <mmsystem.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
#pragma comment(lib, "winmm.lib")
#pragma comment(lib, "gdi32.lib")
#pragma comment(lib, "user32.lib")
#define UNICODE
#define _UNICODE
#define LOG_FILE L"vehicle_log.txt"
#define TARGET_INTERVAL 20 // 20ms目标间隔
#define LOG_BUFFER_SIZE 4096 // 增加日志缓冲区大小
// ------------------ 全局变量定义 ------------------
volatile uint16_t current_vehicle_speed = 0;
uint8_t can_data[8] = {0};
volatile uint64_t total_distance_mm = 0;
volatile uint32_t last_update_time = 0;
const uint32_t can_update_period = 100; // CAN信号每100ms更新一次
LARGE_INTEGER frequency;
volatile int is_paused = 0;
HWND hStatic = NULL;
HANDLE hUpdateThread = NULL;
DWORD threadId;
HANDLE hExitEvent = NULL;
HANDLE hTimer = NULL;
HANDLE hLogThread = NULL;
HANDLE hLogEvent = NULL;
// 日志缓冲区和索引
static wchar_t log_buffer[LOG_BUFFER_SIZE];
static size_t log_index = 0;
static CRITICAL_SECTION log_cs; // 日志缓冲区临界区
// ------------------ 函数声明 ------------------
void ClearLogFile(const wchar_t* log_file);
void BufferLog(uint32_t current_time, uint16_t speed);
void FlushLogBuffer();
void GenerateCANMessage(uint16_t speed_msg, uint8_t frame[8]);
uint16_t ReadCANMessage(uint8_t frame[8]);
void UpdateVehicleState(uint32_t current_time, uint32_t actual_interval);
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
DWORD WINAPI UpdateThread(LPVOID lpParam);
DWORD WINAPI LogThread(LPVOID lpParam);
// ------------------ 清空日志文件 ------------------
void ClearLogFile(const wchar_t* log_file) {
FILE* file = _wfopen(log_file, L"w");
total_distance_mm = 0;
if (file) {
fwprintf(file, L"Log initialized. Vehicle Logger started.\n");
fclose(file);
} else {
OutputDebugStringW(L"无法创建日志文件: vehicle_log.txt\n");
}
}
// ------------------ 缓冲日志 ------------------
void BufferLog(uint32_t current_time, uint16_t speed, uint32_t actual_interval) {
static uint32_t last_log_time = 0;
static uint32_t min_interval = 1000, max_interval = 0;
static uint32_t accumulated_interval = 0;
static uint32_t interval_count = 0;
// 计算实际间隔
uint32_t interval = 0;
if (last_log_time > 0) {
interval = current_time - last_log_time;
if (interval < min_interval) min_interval = interval;
if (interval > max_interval) max_interval = interval;
// 计算平均间隔
accumulated_interval += interval;
interval_count++;
}
last_log_time = current_time;
SYSTEMTIME st;
GetLocalTime(&st);
EnterCriticalSection(&log_cs);
// 计算剩余缓冲区空间
size_t remaining = LOG_BUFFER_SIZE - log_index - 1;
if (interval > 0) {
int written = swprintf_s(log_buffer + log_index, remaining,
L"%02d:%02d:%02d.%03d - Speed: %d km/h, Total Distance: %llu mm, Interval: %dms (Actual: %dms, Min: %d, Max: %d, Avg: %.1f)\n",
st.wHour, st.wMinute, st.wSecond, st.wMilliseconds,
speed, total_distance_mm, interval, actual_interval, min_interval, max_interval,
(float)accumulated_interval / interval_count);
if (written > 0 && written < (int)remaining) {
log_index += written;
} else {
// 缓冲区不足,触发日志刷新
SetEvent(hLogEvent);
swprintf_s(log_buffer + log_index, LOG_BUFFER_SIZE - log_index - 1,
L"%02d:%02d:%02d.%03d - Speed: %d km/h, Total Distance: %llu mm, Interval: %dms (Actual: %dms, Min: %d, Max: %d, Avg: %.1f)\n",
st.wHour, st.wMinute, st.wSecond, st.wMilliseconds,
speed, total_distance_mm, interval, actual_interval, min_interval, max_interval,
(float)accumulated_interval / interval_count);
log_index = wcslen(log_buffer);
}
} else {
// 第一次记录
int written = swprintf_s(log_buffer + log_index, remaining,
L"%02d:%02d:%02d.%03d - Speed: %d km/h, Total Distance: %llu mm\n",
st.wHour, st.wMinute, st.wSecond, st.wMilliseconds,
speed, total_distance_mm);
if (written > 0 && written < (int)remaining) {
log_index += written;
} else {
SetEvent(hLogEvent);
swprintf_s(log_buffer + log_index, LOG_BUFFER_SIZE - log_index - 1,
L"%02d:%02d:%02d.%03d - Speed: %d km/h, Total Distance: %llu mm\n",
st.wHour, st.wMinute, st.wSecond, st.wMilliseconds,
speed, total_distance_mm);
log_index = wcslen(log_buffer);
}
}
// 如果缓冲区接近满,触发日志刷新
if (log_index > LOG_BUFFER_SIZE - 512) {
SetEvent(hLogEvent);
}
LeaveCriticalSection(&log_cs);
}
// ------------------ 刷新日志缓冲区 ------------------
void FlushLogBuffer() {
if (log_index == 0) return;
FILE* file = _wfopen(LOG_FILE, L"a");
if (file) {
fwrite(log_buffer, sizeof(wchar_t), log_index, file);
fclose(file);
log_index = 0;
}
}
// ------------------ 日志线程 ------------------
DWORD WINAPI LogThread(LPVOID lpParam) {
while (1) {
// 等待日志事件或退出事件
HANDLE handles[2] = { hLogEvent, hExitEvent };
DWORD waitResult = WaitForMultipleObjects(2, handles, FALSE, 1000); // 1秒超时
if (waitResult == WAIT_OBJECT_0) {
// 日志事件触发
EnterCriticalSection(&log_cs);
FlushLogBuffer();
LeaveCriticalSection(&log_cs);
}
else if (waitResult == WAIT_OBJECT_0 + 1) {
// 退出事件触发
break;
}
else if (waitResult == WAIT_TIMEOUT) {
// 超时,定期刷新日志
EnterCriticalSection(&log_cs);
if (log_index > 0) {
FlushLogBuffer();
}
LeaveCriticalSection(&log_cs);
}
}
// 退出前确保所有日志都写入
EnterCriticalSection(&log_cs);
FlushLogBuffer();
LeaveCriticalSection(&log_cs);
return 0;
}
// ------------------ 生成 CAN 消息 ------------------
void GenerateCANMessage(uint16_t speed_msg, uint8_t frame[8]) {
memset(frame, 0, 8);
uint16_t mask = (1 << 13) - 1;
uint16_t encoded_speed = speed_msg & mask;
for (int i = 0; i < 13; i++) {
uint16_t bit_pos = 16 + i;
uint8_t byte_pos = bit_pos / 8;
uint8_t bit_in_byte = 7 - (bit_pos % 8);
if (encoded_speed & (1 << i)) {
frame[byte_pos] |= (1 << bit_in_byte);
}
}
}
// ------------------ 读取 CAN 消息 ------------------
uint16_t ReadCANMessage(uint8_t frame[8]) {
uint16_t speed = 0;
for (int i = 0; i < 13; i++) {
uint16_t bit_pos = 16 + i;
uint8_t byte_pos = bit_pos / 8;
uint8_t bit_in_byte = 7 - (bit_pos % 8);
if (frame[byte_pos] & (1 << bit_in_byte)) {
speed |= (1 << i);
}
}
return speed;
}
// ------------------ 更新车辆状态 ------------------
void UpdateVehicleState(uint32_t current_time, uint32_t actual_interval) {
static uint32_t last_can_update_time = 0;
static uint32_t last_log_time = 0;
static LARGE_INTEGER last_counter;
if (is_paused) return;
// 更新CAN信号(每100ms更新一次)
if ((last_can_update_time == 0) || (current_time - last_can_update_time) >= can_update_period) {
uint16_t random_speed = rand() % 150;
GenerateCANMessage(random_speed, can_data);
current_vehicle_speed = random_speed;
last_can_update_time = current_time;
}
// 正确的距离计算公式,使用实际间隔时间
total_distance_mm += (uint64_t)((double)current_vehicle_speed * actual_interval / 3.6);
// 缓冲日志(每20ms)
BufferLog(current_time, current_vehicle_speed, actual_interval);
}
// ------------------ 更新线程 ------------------
DWORD WINAPI UpdateThread(LPVOID lpParam) {
HWND hwnd = (HWND)lpParam;
LARGE_INTEGER start_counter, current_counter, last_counter;
QueryPerformanceCounter(&start_counter);
last_counter = start_counter;
// 设置线程优先级为高,确保精确计时
SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL);
// 设置系统定时器精度
TIMECAPS tc;
timeGetDevCaps(&tc, sizeof(tc));
UINT timerResolution = min(max(tc.wPeriodMin, 1), tc.wPeriodMax);
timeBeginPeriod(timerResolution);
// 创建高精度定时器
HANDLE hTimer = CreateWaitableTimer(NULL, TRUE, NULL);
if (!hTimer) {
OutputDebugStringW(L"无法创建高精度定时器\n");
return 1;
}
LARGE_INTEGER dueTime;
dueTime.QuadPart = -1 * (LONGLONG)(TARGET_INTERVAL * 10000); // 转换为100纳秒单位
// 初始设置定时器
SetWaitableTimer(hTimer, &dueTime, 0, NULL, NULL, FALSE);
uint32_t last_ui_update = 0;
uint32_t last_vehicle_update = 0;
// 初始UI更新
wchar_t init_buffer[256];
swprintf_s(init_buffer, 256, L"当前速度: %d km/h\n总距离: %llu mm",
current_vehicle_speed, total_distance_mm);
PostMessageW(hStatic, WM_SETTEXT, 0, (LPARAM)init_buffer);
// 初始CAN更新
uint16_t random_speed = rand() % 150;
GenerateCANMessage(random_speed, can_data);
current_vehicle_speed = random_speed;
// 性能监控变量
uint32_t min_actual_interval = 1000, max_actual_interval = 0;
uint32_t accumulated_actual_interval = 0;
uint32_t interval_count = 0;
while (1) {
// 检查退出事件
if (WaitForSingleObject(hExitEvent, 0) == WAIT_OBJECT_0)
break;
// 等待定时器触发
DWORD waitResult = WaitForSingleObject(hTimer, INFINITE);
if (waitResult != WAIT_OBJECT_0) {
// 处理超时错误
wchar_t error_msg[128];
swprintf_s(error_msg, 128, L"定时器等待失败: %d", GetLastError());
OutputDebugStringW(error_msg);
continue;
}
// 获取当前时间(毫秒)
QueryPerformanceCounter(¤t_counter);
uint32_t elapsed_ms = (uint32_t)((current_counter.QuadPart - start_counter.QuadPart) * 1000 / frequency.QuadPart);
// 计算实际间隔时间(使用高精度计数器)
uint32_t actual_interval = (uint32_t)((current_counter.QuadPart - last_counter.QuadPart) * 1000 / frequency.QuadPart);
last_counter = current_counter;
// 更新性能统计
if (actual_interval < min_actual_interval) min_actual_interval = actual_interval;
if (actual_interval > max_actual_interval) max_actual_interval = actual_interval;
accumulated_actual_interval += actual_interval;
interval_count++;
// 更新车辆状态(使用实际间隔时间)
UpdateVehicleState(elapsed_ms, actual_interval);
// 100ms UI更新
if (elapsed_ms - last_ui_update >= 100) {
wchar_t buffer[256];
// 显示性能统计
swprintf_s(buffer, 256,
L"速度: %d km/h\n距离: %llu mm\n实际间隔: %dms\n(Min: %d, Max: %d, Avg: %.1f)",
current_vehicle_speed, total_distance_mm,
actual_interval, min_actual_interval, max_actual_interval,
(float)accumulated_actual_interval / interval_count);
PostMessageW(hStatic, WM_SETTEXT, 0, (LPARAM)buffer);
last_ui_update = elapsed_ms;
}
// 重置定时器
SetWaitableTimer(hTimer, &dueTime, 0, NULL, NULL, FALSE);
}
// 清理资源
CloseHandle(hTimer);
timeEndPeriod(timerResolution);
// 确保所有日志都写入文件
SetEvent(hLogEvent);
return 0;
}
// ------------------ 窗口处理函数 ------------------
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
switch (uMsg) {
case WM_CREATE: {
// 初始化日志临界区
InitializeCriticalSection(&log_cs);
// 创建事件对象
hExitEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
hLogEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
ClearLogFile(LOG_FILE);
// 创建按钮控件
CreateWindowW(L"BUTTON", L"Pause",
WS_TABSTOP | WS_VISIBLE | WS_CHILD | BS_DEFPUSHBUTTON,
20, 20, 120, 40, hwnd, (HMENU)1, NULL, NULL);
// 创建静态文本控件
hStatic = CreateWindowW(L"STATIC", L"当前速度: 0 km/h\n总距离: 0 mm",
WS_VISIBLE | WS_CHILD | SS_LEFT,
20, 80, 350, 100, hwnd, (HMENU)2, NULL, NULL);
QueryPerformanceFrequency(&frequency);
GenerateCANMessage(0, can_data);
srand((unsigned int)time(NULL));
// 创建日志线程
hLogThread = CreateThread(
NULL,
0,
LogThread,
NULL,
0,
NULL
);
// 创建更新线程
hUpdateThread = CreateThread(
NULL,
0,
UpdateThread,
(LPVOID)hwnd,
0,
&threadId
);
if (!hUpdateThread) {
MessageBoxW(hwnd, L"无法创建更新线程", L"错误", MB_ICONERROR);
}
break;
}
case WM_COMMAND: {
if (LOWORD(wParam) == 1) {
is_paused = !is_paused;
SetWindowTextW(GetDlgItem(hwnd, 1), is_paused ? L"Resume" : L"Pause");
}
break;
}
case WM_DESTROY: {
// 通知线程退出
SetEvent(hExitEvent);
SetEvent(hLogEvent);
// 等待线程退出
WaitForSingleObject(hUpdateThread, 1000);
WaitForSingleObject(hLogThread, 1000);
// 关闭句柄
CloseHandle(hUpdateThread);
CloseHandle(hLogThread);
CloseHandle(hExitEvent);
CloseHandle(hLogEvent);
// 清理日志临界区
DeleteCriticalSection(&log_cs);
PostQuitMessage(0);
break;
}
default:
return DefWindowProcW(hwnd, uMsg, wParam, lParam);
}
return 0;
}
// ------------------ 主程序 ------------------
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine, int nCmdShow) {
// 确保正确注册窗口类
WNDCLASSW wc = {0};
wc.lpfnWndProc = WindowProc;
wc.hInstance = hInstance;
wc.lpszClassName = L"VehicleLogger";
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.style = CS_HREDRAW | CS_VREDRAW;
if (!RegisterClassW(&wc)) {
MessageBoxW(NULL, L"窗口类注册失败!", L"错误", MB_ICONERROR);
return 0;
}
HWND hwnd = CreateWindowExW(
0,
wc.lpszClassName,
L"CAN信号记录器",
WS_OVERLAPPEDWINDOW | WS_VISIBLE,
CW_USEDEFAULT, CW_USEDEFAULT,
600, 400,
NULL,
NULL,
hInstance,
NULL
);
if (!hwnd) {
MessageBoxW(NULL, L"窗口创建失败!", L"错误", MB_ICONERROR);
return 0;
}
ShowWindow(hwnd, nCmdShow);
UpdateWindow(hwnd);
MSG msg;
while (GetMessageW(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessageW(&msg);
}
return (int)msg.wParam;
}
```
## 关键改进点
### 1. 独立的日志线程
```c
DWORD WINAPI LogThread(LPVOID lpParam) {
while (1) {
HANDLE handles[2] = { hLogEvent, hExitEvent };
DWORD waitResult = WaitForMultipleObjects(2, handles, FALSE, 1000);
if (waitResult == WAIT_OBJECT_0) {
EnterCriticalSection(&log_cs);
FlushLogBuffer();
LeaveCriticalSection(&log_cs);
}
// ...
}
}
```
将日志文件写入操作移到单独的线程,避免I/O操作阻塞主定时线程
### 2. 精确的时间间隔计算
```c
// 计算实际间隔时间(使用高精度计数器)
uint32_t actual_interval = (uint32_t)((current_counter.QuadPart - last_counter.QuadPart) * 1000 / frequency.QuadPart);
last_counter = current_counter;
// 使用实际间隔时间计算距离
total_distance_mm += (uint64_t)((double)current_vehicle_speed * actual_interval / 3.6);
```
使用高精度计数器计算实际经过的时间,而不是依赖系统时间
### 3. 性能监控与显示
```c
// 性能监控变量
uint32_t min_actual_interval = 1000, max_actual_interval = 0;
uint32_t accumulated_actual_interval = 0;
uint32_t interval_count = 0;
// 在UI中显示性能数据
swprintf_s(buffer, 256,
L"速度: %d km/h\n距离: %llu mm\n实际间隔: %dms\n(Min: %d, Max: %d, Avg: %.1f)",
current_vehicle_speed, total_distance_mm,
actual_interval, min_actual_interval, max_actual_interval,
(float)accumulated_actual_interval / interval_count);
```
实时监控并显示定时精度,帮助诊断问题
### 4. 增强的日志格式
```c
swprintf_s(...,
L"... Interval: %dms (Actual: %dms, Min: %d, Max: %d, Avg: %.1f)\n",
interval, actual_interval, min_interval, max_interval,
(float)accumulated_interval / interval_count);
```
在日志中记录目标间隔和实际间隔,便于分析时间跳变原因
### 5. 资源管理优化
```c
case WM_DESTROY: {
SetEvent(hExitEvent);
SetEvent(hLogEvent);
WaitForSingleObject(hUpdateThread, 1000);
WaitForSingleObject(hLogThread, 1000);
// ...
}
```
确保所有线程都能正确退出,所有资源都被释放
## 编译命令
```bash
gcc -fdiagnostics-color=always -g T5_3.c -o T5_3.exe -lgdi32 -lwinmm -luser32 -mwindows
```
## 高级优化:使用多媒体定时器API
如果仍然遇到定时精度问题,可以使用多媒体定时器API替代WaitableTimer:
```c
// 在UpdateThread中替换定时器部分:
// 创建多媒体定时器
MMRESULT timerId = timeSetEvent(
TARGET_INTERVAL,
timerResolution,
(LPTIMECALLBACK)TimerCallback,
(DWORD_PTR)this,
TIME_PERIODIC | TIME_CALLBACK_FUNCTION
);
// 定时器回调函数
void CALLBACK TimerCallback(UINT uTimerID, UINT uMsg, DWORD_PTR dwUser, DWORD_PTR dw1, DWORD_PTR dw2) {
// 获取当前时间
LARGE_INTEGER current_counter;
QueryPerformanceCounter(¤t_counter);
// 计算实际间隔
static LARGE_INTEGER last_counter;
uint32_t actual_interval = (uint32_t)((current_counter.QuadPart - last_counter.QuadPart) * 1000 / frequency.QuadPart);
last_counter = current_counter;
// 更新车辆状态
uint32_t elapsed_ms = (uint32_t)((current_counter.QuadPart - start_counter.QuadPart) * 1000 / frequency.QuadPart);
UpdateVehicleState(elapsed_ms, actual_interval);
// UI更新(每100ms)
static uint32_t last_ui_update = 0;
if (elapsed_ms - last_ui_update >= 100) {
// ... UI更新代码 ...
last_ui_update = elapsed_ms;
}
}
// 在退出时清理定时器
timeKillEvent(timerId);
```
## 预防性措施
### 1. 时间跳变补偿
```c
// 在距离计算中增加时间跳变检测
if (actual_interval > TARGET_INTERVAL * 2) {
// 使用最大合理间隔(例如50ms)计算距离
uint32_t compensated_interval = min(actual_interval, 50);
total_distance_mm += (uint64_t)((double)current_vehicle_speed * compensated_interval / 3.6);
// 记录异常事件
wchar_t warn_msg[128];
swprintf_s(warn_msg, 128, L"警告:时间跳变 %dms > %dms", actual_interval, TARGET_INTERVAL * 2);
OutputDebugStringW(warn_msg);
} else {
total_distance_mm += (uint64_t)((double)current_vehicle_speed * actual_interval / 3.6);
}
```
### 2. 优先级提升
```c
// 设置线程优先级为实时级别
SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL);
// 提升进程优先级
SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS);
```
### 3. 核心绑定
```c
// 将线程绑定到特定CPU核心
DWORD_PTR affinityMask = 1; // 使用第一个核心
SetThreadAffinityMask(GetCurrentThread(), affinityMask);
```
## 性能监控建议
1. **实时性能显示**:
```c
// 在UI更新中添加
swprintf_s(buffer, 256,
L"速度: %d km/h\n距离: %llu mm\n实际间隔: %dms\n(Min: %d, Max: %d, Avg: %.1f)\n抖动: %.2fms",
current_vehicle_speed, total_distance_mm,
actual_interval, min_actual_interval, max_actual_interval,
(float)accumulated_actual_interval / interval_count,
(float)(max_actual_interval - min_actual_interval));
```
2. **自动降级策略**:
```c
// 如果平均间隔超过阈值,降低日志频率
float avg_interval = (float)accumulated_actual_interval / interval_count;
if (avg_interval > TARGET_INTERVAL * 1.5) {
// 每2次更新记录一次日志
static bool skip_log = false;
if (skip_log) {
skip_log = false;
} else {
BufferLog(...);
skip_log = true;
}
}
```
3. **资源监控**:
```c
// 显示内存和CPU使用情况
PROCESS_MEMORY_COUNTERS pmc;
if (GetProcessMemoryInfo(GetCurrentProcess(), &pmc, sizeof(pmc))) {
swprintf_s(buffer + wcslen(buffer), 256 - wcslen(buffer),
L"\n内存: %dKB", pmc.WorkingSetSize / 1024);
}
```