如何在主线程中等待函数终止?视频库回调同步问题求助
解决方案
1. 重构同步逻辑:用事件替代原子变量实现可靠同步
放弃TThread::Synchronize的隐式消息同步,改用手动事件+主线程主动轮询的方式,让库线程等待主线程完成任务,避免主线程阻塞。
首先在类中添加成员变量:
class TStreamToVideo : public TCustomControl { // ... 原有成员 ... HANDLE FSyncEvent; // 通知主线程有任务待处理 HANDLE FCallbackDoneEvent; // 通知库线程任务已完成 std::mutex FCallbackMutex; // 保护任务对象的线程安全 std::function<void()> FSyncTask; // 待主线程执行的任务 };
2. 修改构造/析构函数初始化资源
TStreamToVideo::TStreamToVideo(TWinControl *Owner, const String &DllPath) : TCustomControl(Owner), Lib(DllPath.c_str()), Stream(NULL), FSyncEvent(CreateEvent(nullptr, FALSE, FALSE, nullptr)), FCallbackDoneEvent(CreateEvent(nullptr, FALSE, FALSE, nullptr)), FSyncTask(nullptr) { Parent = Owner; Align = alClient; Lib.Set(this->Handle); // 启动10ms定时器,让主线程主动检查任务 SetTimer(Handle, 1, 10, nullptr); } TStreamToVideo::~TStreamToVideo() { KillTimer(Handle, 1); // 先停止定时器 Stop(); // 安全停止播放 // 清理系统资源 CloseHandle(FSyncEvent); CloseHandle(FCallbackDoneEvent); }
3. 重写回调函数,用事件同步任务
以ReadCallback为例,修改为:
int TStreamToVideo::ReadCallback(void *data, unsigned char *buf, size_t len) { int Result = -1; TStreamToVideo* Player = static_cast<TStreamToVideo*>(data); if (!Player) return Result; // 先检查停止标志,直接退出 if (Player->StopCallbacks.load()) return -1; std::lock_guard<std::mutex> lock(Player->FCallbackMutex); Player->CallbacksActive.store(true); // 打包主线程要执行的任务 Player->FSyncTask = [Player, &Result, buf, len]() { try { if (!Player->StopCallbacks.load()) Result = Player->Stream->Read(buf, (System::NativeInt)len); } catch (...) {} // 任务完成后触发事件,通知库线程 SetEvent(Player->FCallbackDoneEvent); }; // 通知主线程有任务要处理 SetEvent(Player->FSyncEvent); // 库线程等待主线程完成任务(这里不会阻塞主线程) WaitForSingleObject(Player->FCallbackDoneEvent.Handle, INFINITE); Player->CallbacksActive.store(false); return Result; }
SeekCallback做相同修改即可。
4. 主线程处理任务:重写WndProc响应定时器
void TStreamToVideo::WndProc(TMessage &Msg) { if (Msg.Msg == WM_TIMER && Msg.WParam == 1) { // 检查是否有等待处理的任务 if (WaitForSingleObject(FSyncEvent, 0) == WAIT_OBJECT_0) { std::lock_guard<std::mutex> lock(FCallbackMutex); if (FSyncTask) { FSyncTask(); // 执行主线程任务 FSyncTask = nullptr; ResetEvent(FCallbackDoneEvent); // 重置事件 } } } TCustomControl::WndProc(Msg); // 处理原有消息 }
5. 安全的停止流程
修改Stop()函数,确保等待回调完成后再停止库:
void TStreamToVideo::Stop(void) { StopCallbacks.store(true); // 等待回调完成,最多等待1秒避免死锁 DWORD waitResult = WaitForSingleObject(FCallbackDoneEvent, 1000); if (waitResult == WAIT_OBJECT_0 || !CallbacksActive.load()) { Lib.Stop(); } else { // 极端情况强制停止,可添加日志记录 MessageBox(NULL, L"回调未及时完成,已强制停止", L"提示", 0); Lib.Stop(); } }
关键改进说明
- 让库线程等待主线程完成任务,而非主线程等待库线程,彻底避免主线程阻塞死锁
- 用事件替代原子变量,实现可靠的任务完成通知,解决
CallbacksActive状态不一致的问题 - 主线程通过定时器主动处理任务,比
TThread::Synchronize的隐式消息更可控,避免CheckSynchronize()无效的问题
内容的提问来源于stack exchange,提问作者Peter
相关产品推荐
相关产品推荐

