C++:如何异步调用同步库调用
C++: How to call a synchronous library call asynchronously?
我使用的库有一个阻塞调用,如果不成功,它永远不会超时。我希望能够更优雅地处理这种错误情况。我知道必须有一种方法将调用封装在工作线程(或其他类型的委托对象)中,等待x秒,然后在x秒过去时抛出异常。我只需要为库中的一个函数执行此操作。我该如何着手实现这一点?我在网上看到了类似的例子,但没有一个正是我想要做的。谢谢!
我的答案是"不要尝试这样做"。
当然,你可能会发现一些黑客在你的特定情况下会起作用。但是这里的比赛条件很难解决。
显而易见的方法是让线程A
进行阻塞调用,然后设置线程B
在超时时杀死A
。
但是。。。如果超时在A
从阻塞调用返回的同时到期,该怎么办?具体来说,如果B
认为是时候杀死A
了,那么您的操作系统调度程序决定运行A
一段时间,然后您的操作程序决定运行杀死A
的B
代码,该怎么办?
一句话:你最终会在A
执行过程中的某个不确定点杀死它。(例如,也许它刚刚从储蓄账户中扣除了500美元,但还没有向支票账户中增加500美元。可能性是无限的…)
好吧,这样您就可以让线程A
存在,其唯一目的是运行库调用,然后在调用结束时发出条件或其他信号。至少在原则上这是可行的。但即便如此,如果库本身有一些内部状态处于不一致状态,A
是否会在不合时宜的时刻被杀死呢?
C++11标准中省略了异步线程取消是有充分理由的。只需说不。修复库例程。无论花费多少,从长远来看,它几乎肯定比你正在尝试的更便宜。
使用C++11,然后为该调用显式启动线程,看起来可能是:
// API
T call(int param);
// asynchronous call with 42 as parameter
auto future = std::async(std::launch::async, call, 42);
// let's wait for 40 ms
auto constexpr duration = std::chrono::milliseconds(40);
if(future.wait_for(duration) == std::future_status::timeout) {
// We waited for 40ms and had a timeout, now what?
} else {
// Okay, result is available through future.get()
// if call(42) threw an exception then future.get() will
// rethrow that exception so it's worth doing even if T is void
future.get();
}
正如你所看到的,在超时的情况下,你会遇到一个大问题,因为你永远都被阻塞的线程卡住了。这可以说不是C++11std::future
的错:相当数量的线程抽象最多只能提供协作取消,但这仍然不足以拯救您。
如果你没有使用C++11,那么Boost.Thread与boost::unique_future
有一个非常相似的接口(其中wait_for
是timed_wait
,并返回bool
),尽管它没有类似于std::async
的东西,所以你必须自己做一些繁忙的工作(例如boost::packaged_task
+boost::thread
)。文档中提供了详细信息。
显然,进行阻塞调用的线程不能自行终止,它将被阻塞。
一种方法是启动进行阻塞调用的线程a,然后启动另一个在超时期间休眠的线程B,然后杀死线程a。受互斥体保护的共享标志可以指示操作是否成功,基于此可以引发异常。
第二种方法(非常类似)是启动线程A,线程A依次启动线程B,休眠超时,然后杀死线程B。
首选线程库的细节(例如允许哪些线程相互杀死)和阻塞函数的性质将确切地影响您如何执行此操作。
在Windows上,您会想要做这样的事情:
//your main thread
DWORD threadID;
HANDLE h = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)&ThreadProc, 0, 0, &threadID);
DWORD ret = 0xFFFFFF;
for (int i = 0; i < /*some timeout*/; i++) {
ret = WaitForSingleObject(h, 100);
if (ret == WAIT_OBJECT_0) break;
}
if (ret != WAIT_OBJECT_0) {
DWORD exitCode;
TerminateThread(h, &exitCode); // you will want to stop the thread as it isn't exiting.
/*throw*/;
}
和
//Thread Routine
DWORD ThreadProc (LPVOID threadParam) {
//call your function here
return 0;
}
这里的想法是旋转一个线程来做你想要的工作。然后您可以在中等待该线程100毫秒的增量(或任何您想要的)。如果它没有在特定的时间段内结束,您可以抛出一个异常。
存在一些问题。首先,库是否保持任何内部状态,这些状态将因库调用失败而不可用?如果是这样的话,你就会被绊倒,因为被阻止的失败调用之后的调用也会失败,或者更糟的是,在没有任何异常或其他通知的情况下生成错误的结果。
如果库是安全的,那么您确实可以尝试线程关闭调用,并等待某个超时事件。现在你需要处理@Nemo的担忧——你需要注意如何处理结果的返回。具体如何做到这一点取决于,嗯,您打算如何从调用库的线程返回结果。通常,两个线程都会进入一个关键部分,在返回结果的lib线程和指示lib线程永远不返回任何东西的超时线程之间进行安全仲裁(例如,通过在其中设置标志),如果lib调用返回,则退出。
孤立自由。线程就是这样一种方式,如果lib调用永远不会返回,就会导致线程泄漏。您是否能够吸收此类泄漏,或者安全地最终强制终止孤立线程,取决于您和您的应用程序:)
- 什么时候调用组成单元对象的析构函数
- 当对套接字 send() 的同步调用由于连接另一端丢失而被阻止时,如何恢复?
- ofstream::close() 是否在 Linux 上调用同步?
- Vulkan是否需要在多个具有透明度的平局调用之间进行同步
- 关于C++ REST SDK 和同步调用
- 异步操作的 Asio 处理程序在其同步对应项正常工作时不会调用
- 使用 P/调用传递取消标志时是否需要同步
- 从多个线程对共享对象的同步方法调用
- 嵌入在c中的Python.正在调用pyrun_simplestring同步
- 可以使对AudioObjectSetPropertyData的调用同步吗
- 如何从另一个线程的 cpp 代码同步调用 qml 信号处理程序
- C++:如何异步调用同步库调用
- 为什么std::async即使指定了std::launch::async标志,也要同步调用函数?
- 同步来自多个对象的异步调用
- 对运算符的调用是否'delete'同步的?
- 当调用ReadDirectoryChangesW时,只有第一次调用返回任何更改(同步和异步)
- c++ /Boost:跨多个方法(getter)调用同步对资源的访问
- 是boost::信号槽被同步或异步调用
- 使用异步调用与在线程中使用同步调用相同
- 如何使用 MethodInvoker 委托在 cli c++ 中同步对控件线程的调用