- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我正在开发一个与 C API 兼容的库。
在库中,将有一个对象的全局实例,该对象的成员是 std::thread
。似乎由于某种原因,当 main
返回并调用 exit()
时,线程会自动终止/终止。如果代码在同一项目中使用(直接在可执行文件中而不是通过库),则不会发生这种情况。
我希望以下示例在无限循环中运行:while(1) {...}
和 thread.join()
应该相互阻塞。当它与库一起使用时,它不会,当调用 CThread
的析构函数时,线程似乎已经被终止/完成。我在这里缺少什么?
CThread.h
#ifndef CTHREAD_H
#define CTHREAD_H
#ifdef EXPORT_C_THREAD
# define EXPORT_CTHREAD __declspec(dllexport) __cdecl
#else
# define EXPORT_CTHREAD __declspec(dllimport) __cdecl
#endif
void EXPORT_CTHREAD testFunc();
#endif
CThread.cpp
#include "CThread.h"
#include <thread>
#include <memory>
class CThread
{
std::thread m_thread;
void infiniteLoop()
{
while (1) {
std::this_thread::sleep_for(std::chrono::microseconds(100));
}
}
public:
CThread()
{
m_thread = std::thread(&CThread::infiniteLoop, this);
}
~CThread()
{
m_thread.join();
}
};
std::unique_ptr<CThread> cthread;
void testFunc()
{
cthread = std::make_unique<CThread>();
}
main.cpp(这是在另一个项目中。我正在链接到上面的库。)
#include "CThread.h"
int main()
{
testFunc();
return 0;
}
更新
按照建议,我尝试在 DLL_PROCESS_ATTACH
期间初始化 DllMain()
函数中的 cthread
对象,并在 期间解除分配>DLL_PROCESS_DETACH
。由于 DllMain()
函数将获取加载程序锁,因此我必须稍后初始化线程。但是,和以前一样,当“调用”DLL_PROCESS_DETACH
时线程已经中止。 DLL_THREAD_DETACH
不会在退出时被调用。
还有什么建议吗?谢谢!
最佳答案
我能够使用 VS2015 重现此行为。
问题出在 std::unique_ptr<CThread> pthread;
作为一个全局对象。指针的删除与 main
的执行无关线。 dll 的全局数据和主机 exe 的全局数据不会以可预测的方式相互同步。引入线程时还有更多的复杂性,这些线程会在进程退出时停止。
正如您所注意到的,将所有代码移动到单个 exe 允许全局数据在 main
上适当同步线程。
要解决这个问题,您可以导出一个 RAII 样式的类来管理线程的执行,或者简单地提供一个“清除线程”或清理函数并将其导出;该函数将具有以下形式;
void cleanupData()
{
cthread = nullptr; // block waiting for thread exit
}
为了进一步协助客户端代码,仍然可以提供支持此“清晰线程”的 RAII 类,但无需从 dll 中导出它。
客户端 RAII 可以利用 std::unique_ptr
或 std::shared_ptr
按要求。最简单的形式是这样的;
struct Cleanup {
Cleanup() = default;
Cleanup(Cleanup const&) = delete;
Cleanup& operator=(Cleanup const&) = delete;
Cleanup(Cleanup&&) = delete;
Cleanup& operator=(Cleanup&&) = delete;
~Cleanup() { cleanupData(); } // clean up the threads...
};
这需要与开始使用或从 dll 导入的数据的生命周期相关联。
并用作;
{
auto cleanup = std::make_unique<Cleanup>();
testFunc();
// ...
}
题外话在析构函数中,在你之前join()
线程,测试以确保它是 joinable()
.
鉴于更新;这可以更改或改进吗,即一旦执行离开就可以控制线程 main
? TL;DR,不。
以“旧新事物”成名的陈峰,引自here :
On the other hand, the C runtime library automatically calls
ExitProcess
when you exit themain
thread, regardless of whether there are any worker threads still active. This behavior for console programs is mandated by the C language, which says that (5.1.2.2.3) “a return from the initial call to themain
function is equivalent to calling the exit function with the value returned by themain
function as its argument.” The C++ language has an equivalent requirement (3.6.1). Presumably, the C runtime folks carried this behavior toWinMain
for consistency.
什么是 ExitProcess()
do ?
...
- All of the threads in the process, except the calling thread, terminate their execution without receiving a
DLL_THREAD_DETACH
notification.- The states of all of the threads terminated in step 1 become signaled.
- The entry-point functions of all loaded dynamic-link libraries (DLLs) are called with
DLL_PROCESS_DETACH
....
特别是上面的第 1 点和第 3 点,到 std::unique_ptr<CThread>
的析构函数时运行时,线程已经停止并发出信号。一旦主线程调用了 ExitProcess()
,就不能依赖后台线程执行,它们已经停止,操作系统正在清理与该进程关联的所有资源。
你在评论中提到;
I will use an IPC mutex within the thread so it's critical that is cleaned up properly.
如果问题是互斥量而不仅仅是线程,那么问题就会稍微安静一些。然后更容易将互斥锁提取到全局级别并允许 DllMain
到(使用 DLL_PROCESS_ATTACH
等)来管理它。线程将被允许正常访问。可能有一些额外的代码来指示互斥体的状态,但也可以使用 dll 进行管理。
同时请记住,IPC 互斥机制通常包括一个“已放弃”状态,正是为了这个目的。如果互斥锁的持有者意外失败并放弃互斥锁,则在尝试访问互斥锁时,互斥锁的其余客户端会收到通知。
来自WIN32 Mutex :
If a thread terminates without releasing its ownership of a mutex object, the mutex object is considered to be abandoned. A waiting thread can acquire ownership of an abandoned mutex object, but the wait function will return
WAIT_ABANDONED
to indicate that the mutex object is abandoned...
关于c++ - 线程在完成执行代码之前在动态库中中止,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36408581/
http://www.vex.net/~trebla/haskell/so.xhtml描述如何编译共享库。 关于编译命令: ghc -O2 -dynamic -shared -fPIC -o libE
我想从Rust调用一个C++动态库(* .so),但是我不想从Rust构建它。像这样, cc::Build::new() .file("src/foo.cc") .shared_fla
我想问一下,打包在ear中的war是否可以使用war文件中没有打包的库。我想在文件系统上拥有包含库的目录,部署的 war 文件将从该目录加载库。我还没有找到如何执行此操作的示例。所以我想问一下部署的w
我正在制作一个 C++ 库。在库中,我正在使用另一个静态库中的一些函数(例如 ref.a)。 我想生成一个库文件,mylib.a 或 mylib.so,这样使用我的库的任何程序都不需要链接到静态库 (
我正在尝试在 linux 中为一个使用 opencv 和 tesseract 以及动态链接的程序创建一个共享库 我关注了link我的代码如下 g++ -c Serial_Key.cpp -fPIC -
[编辑:简而言之,问题是:当我链接到一个链接到另一个动态库的动态库时,我是否也必须显式链接到那个?] 我在一个软件中看到了类似的东西。它不起作用,现在我想知道它是否应该起作用。我可以将库“bar”动态
我的项目中有一堆 Dll,使用 VStudio 9.0 编译器,预编译头文件用于我所有的 Dll。 dll 的加载过程由隐式调用程序完成(必须提供.dll、.lib 和 header )。 我通常为每
在我开始开发更多的 c++ 项目之前,我想建立一个良好的多平台环境并使用一些基本形式的修订 (rsync)。为此,我学习了如何构建 makefile(使用 uname 变量)并且还开始在 Window
我测试试,导出dll成功了,但是调用exe编译失败。 后来把声明代码改为了dllexport,调用exe编译成功了,调用也成功。 #pragma once #ifdef YOLOFACE_DLL_AP
我的问题很简单。有什么方法可以反编译、逆向工程 DYLIB 文件(MAC 的动态库,如 Windows DLL)。? 推荐什么程序这样做? 我正在寻找类似 Olly 或 IDA 之类的程序,但适用于
我已将我的应用提交到App Store,但遭到拒绝。我收到一封电子邮件,说: 无效的捆绑包-dylib搜索路径中不存在您的应用程序引用的一个或多个动态库。 我没有使用CocoaPods,所以所有外部框
我在 CPython 中使用 pythonnet,并且我成功安装了它 import clr clr.AddReference('Assembly') 确实有效。 在我的 C# 代码中,如果是成员 pu
GCC 简介有一个例子: $ gcc -Wall -L. main.c -lhello -o hello The option ‘-L.’ is needed to add the current d
我知道当您使用 dlopen() 加载动态 C++ 库时,您可以获得指向该库内函数的指针,但是有没有一种方法可以有效地(性能很重要)另一个怎么办? 我知道我可以在库中调用一个函数(在初始化库时),将一
给程序和库添加版本号和库,有利于维护和升级。 当然你可以在文件名上体现,比如有个程序叫 yun,文件名写为 yun_1.0.2,但这个需要每次手动维护,而且不能100%确保当前程序就是那个版本。所
我正在尝试将我的 C 程序链接到静态库和动态库以查看差异。我怎么做?我制作了自己的 Makefile: # ------ executable rule ----------- app : app.
假设我想创建一个动态库 dynamic.so,但我的代码引用了某个其他静态库 static.a 中存在的函数。自然地,如果我使用 g++ 和 -shared 选项进行编译和链接,dynamic.so
我想使用 alarm函数有一个中断来安排对 fcntl + F_SETLKW 的阻塞调用超时(用于锁定文件获取)。但是,我的代码位于共享库/dylib(主机应用程序的插件)中,alarm 的文档指出这
【目录】 第一个动态库文件 应用程序 第二个动态库文件 错误做法:直接给它改名 正解:patchelf 工具 One More Thing
什么是模块化编程 模块化编程就是我们一个复杂的项目分成很多模块,比如一个单片机项目,就可能分为:主函数模块,液晶显示和数码管显示模块,时间延时模块,温度传感器模块等。而一个程序工程包含多个源文件(.c
我是一名优秀的程序员,十分优秀!