草庐IT

c++ - 清理DLL : _endthreadex() vs TerminateThread()中的线程

coder 2024-02-18 原文

由于restrictions on DllMain(我知道这对DLL中的全局和静态对象构造函数和析构函数同样适用),像带异步文件写入/刷新线程的单例记录器这样的简单事情变得太棘手了。单例记录器位于DLL中,并且对可执行文件的加载和卸载时的影响有限。我可以强制该可执行文件在使用前调用它的DLL初始化函数,因此在初始化函数中,我可以使用关键部分来保护一个变量,该变量告诉DLL是否已经初始化或这次是否需要初始化。通过这种方式避免了DllMain的初始化,这将导致死锁,因为我需要从初始化启动线程,并且线程使用DllMain的原因调用DLL_THREAD_ATTACH,并且获得与我们在DllMain上进行初始化时已经获得的加载程序锁相同的加载程序锁。 DLL_PROCESS_ATTACH事件。

由于this bug(MSVC++ 2013中未修复),因此无法使用C++ 11 thread。所以我正在使用_beginthreadex(),因为 CreateThread documentation说:

A thread in an executable that calls the C run-time library (CRT) should use the _beginthreadex and _endthreadex functions for thread management rather than CreateThread and ExitThread; this requires the use of the multithreaded version of the CRT. If a thread created using CreateThread calls the CRT, the CRT may terminate the process in low-memory conditions.



但是我无法控制可执行文件,以确保在卸载DLL之前调用DLL的一些反初始化功能。因此,清理的唯一选项是DllMainDLL_PROCESS_DETACH和全局/静态变量的析构函数。问题在于它们是在获得加载程序锁的情况下调用的,所以我无法使DLL线程在那里正常退出,因为正常退出时那些线程会尝试使用DllMain调用DLL_THREAD_DETACH,这将导致死锁(再次加载程序锁)。 MSDN建议使用TerminateThread()处理此问题:

DLL A gets a DLL_PROCESS_DETACH message in its DllMain and sets an event for thread T, signaling it to exit. Thread T finishes its current task, brings itself to a consistent state, signals DLL A, and waits infinitely. Note that the consistency-checking routines should follow the same restrictions as DllMain to avoid deadlocking. DLL A terminates T, knowing that it is in a consistent state.



因此,我担心使用_beginthreadex() + TerminateThread()对,而不是设计的_endthreadex()(如果线程正常返回,则线程本身将调用后者)。

tl; dr 考虑一个从其入口函数返回的线程与一个在其函数末尾执行Sleep(INFINITE)之类的线程等待终止的线程(即,在获得资源一致并向终止线程发出信号后,该线程已终止)准备好)。如果未调用thread_local而是调用_endthreadex(),是否会导致某些CRT或C++ 11资源(例如TerminatThread())等泄漏或损坏等?

最佳答案

好的。首先,让我们介绍一些要点:

  • 正如David在评论中提到的you don't need to use _beginthreadex() rather than CreateThread()。同样,在任何当前受支持的Visual Studio和Windows版本上,也可以使用ExitThread()或类似名称代替_endthreadex()。
  • 尽管MSDN文章说了什么,the accepted wisdom is that it is never OK to use TerminateThread()
  • 如果您了解加载程序锁所隐含的限制,则通常也可以接受,可以在DllMain的DLL_PROCESS_ATTACH处理中使用CreateThread()。但是,如果能够使用适当的初始化例程而不是DllMain(如您的情况),那就更好了。

  • 因此,如果我正确理解了您的情况,可以总结如下:
  • 您的DLL需要一个或多个后台线程。
  • 可执行文件无需警告就卸载DLL。

  • 这有点愚蠢,但这不是你的错。幸运的是,并非没有可能。

    如果可接受的线程在可执行文件认为已卸载DLL后继续运行,则可以use the FreeLibraryAndExitThread() pattern。在初始化函数以及创建线程的其他任何地方,调用GetModuleHandleEx()来增加DLL引用计数。这样,当可执行文件调用FreeLibrary()时,如果任何线程仍在运行,则该DLL实际上不会被卸载。线程通过调用FreeLibraryAndExitThread()退出,并保持引用计数。

    但是,这种方法可能无法直接满足您的需求,因为它不允许您检测可执行文件何时卸载了库,因此您可以发出信号以终止线程。

    可能会有更聪明的解决方案,但是我建议您使用辅助DLL。这个想法是,帮助程序DLL(而不是主DLL)会跟踪线程引用计数,即,每次创建后台线程时都加载帮助程序DLL,并在每次后台线程退出时将其卸载。辅助DLL只需要包含一个函数,该函数调用SetEvent(),然后调用FreeLibraryAndExitThread()。

    当通知后台线程DLL正在被卸载时,它会清理,然后调用帮助程序DLL来设置事件并退出线程。设置事件后,您的主DLL的分离例程将知道该线程不再运行来自主DLL的代码。一旦每个后台线程完成清理,就可以安全地卸载主DLL-线程仍在运行并不重要,因为它们正在运行的是来自帮助DLL而不是主DLL的代码。一旦最后一个线程调用FreeLibraryAndExitThread(),则辅助DLL将自动卸载。

    大约一年后再来看一次,可能更安全的做法是:使主DLL除了初始化函数和程序正在调用的其他函数外,不包含任何东西,外加一个DllMain,用于向后台线程发送信号。退出,并具有包含其他所有内容的辅助DLL。

    特别是,如果辅助DLL包含您的后台线程需要的所有代码,则在后台线程仍在运行时卸载主DLL是安全的。

    这种变体的优点是,当您的后台线程看到退出信号时,主DLL是否已经卸载都没有关系,因此您的DllMain函数不必在保持加载程序锁定的同时等待。这样,如果后台线程之一无意中执行了需要加载程序锁定的操作,则该过程不会死锁。

    作为同一个想法的变体,如果您确实真的不想在CRT线程上使用FreeLibraryAndExitThread(),则可以在辅助DLL中放置一个额外的线程来协调卸载。该线程将从CreateThread()开始,并且不使用任何CRT函数,因此通过FreeLibraryAndExitThread()退出无疑是安全的。它的唯一职责是在卸载辅助DLL之前等待所有其他线程退出。

    确实不再需要再区分CRT和非CRT线程,但是,如果您要严格遵循所记录的规则,这将是实现此目的的一种方法。

    关于c++ - 清理DLL : _endthreadex() vs TerminateThread()中的线程,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39241400/

    有关c++ - 清理DLL : _endthreadex() vs TerminateThread()中的线程的更多相关文章

    1. ruby - 如何从 ruby​​ 中的字符串运行任意对象方法? - 2

      总的来说,我对ruby​​还比较陌生,我正在为我正在创建的对象编写一些rspec测试用例。许多测试用例都非常基础,我只是想确保正确填充和返回值。我想知道是否有办法使用循环结构来执行此操作。不必为我要测试的每个方法都设置一个assertEquals。例如:describeitem,"TestingtheItem"doit"willhaveanullvaluetostart"doitem=Item.new#HereIcoulddotheitem.name.shouldbe_nil#thenIcoulddoitem.category.shouldbe_nilendend但我想要一些方法来使用

    2. ruby - 其他文件中的 Rake 任务 - 2

      我试图在一个项目中使用rake,如果我把所有东西都放到Rakefile中,它会很大并且很难读取/找到东西,所以我试着将每个命名空间放在lib/rake中它自己的文件中,我添加了这个到我的rake文件的顶部:Dir['#{File.dirname(__FILE__)}/lib/rake/*.rake'].map{|f|requiref}它加载文件没问题,但没有任务。我现在只有一个.rake文件作为测试,名为“servers.rake”,它看起来像这样:namespace:serverdotask:testdoputs"test"endend所以当我运行rakeserver:testid时

    3. ruby-on-rails - Ruby net/ldap 模块中的内存泄漏 - 2

      作为我的Rails应用程序的一部分,我编写了一个小导入程序,它从我们的LDAP系统中吸取数据并将其塞入一个用户表中。不幸的是,与LDAP相关的代码在遍历我们的32K用户时泄漏了大量内存,我一直无法弄清楚如何解决这个问题。这个问题似乎在某种程度上与LDAP库有关,因为当我删除对LDAP内容的调用时,内存使用情况会很好地稳定下来。此外,不断增加的对象是Net::BER::BerIdentifiedString和Net::BER::BerIdentifiedArray,它们都是LDAP库的一部分。当我运行导入时,内存使用量最终达到超过1GB的峰值。如果问题存在,我需要找到一些方法来更正我的代

    4. ruby-on-rails - Rails 3 中的多个路由文件 - 2

      Rails2.3可以选择随时使用RouteSet#add_configuration_file添加更多路由。是否可以在Rails3项目中做同样的事情? 最佳答案 在config/application.rb中:config.paths.config.routes在Rails3.2(也可能是Rails3.1)中,使用:config.paths["config/routes"] 关于ruby-on-rails-Rails3中的多个路由文件,我们在StackOverflow上找到一个类似的问题

    5. ruby-on-rails - Rails - 一个 View 中的多个模型 - 2

      我需要从一个View访问多个模型。以前,我的links_controller仅用于提供以不同方式排序的链接资源。现在我想包括一个部分(我假设)显示按分数排序的顶级用户(@users=User.all.sort_by(&:score))我知道我可以将此代码插入每个链接操作并从View访问它,但这似乎不是“ruby方式”,我将需要在不久的将来访问更多模型。这可能会变得很脏,是否有针对这种情况的任何技术?注意事项:我认为我的应用程序正朝着单一格式和动态页面内容的方向发展,本质上是一个典型的网络应用程序。我知道before_filter但考虑到我希望应用程序进入的方向,这似乎很麻烦。最终从任何

    6. ruby-on-rails - Rails 3.2.1 中 ActionMailer 中的未定义方法 'default_content_type=' - 2

      我在我的项目中添加了一个系统来重置用户密码并通过电子邮件将密码发送给他,以防他忘记密码。昨天它运行良好(当我实现它时)。当我今天尝试启动服务器时,出现以下错误。=>BootingWEBrick=>Rails3.2.1applicationstartingindevelopmentonhttp://0.0.0.0:3000=>Callwith-dtodetach=>Ctrl-CtoshutdownserverExiting/Users/vinayshenoy/.rvm/gems/ruby-1.9.3-p0/gems/actionmailer-3.2.1/lib/action_mailer

    7. ruby-on-rails - 如何优雅地重启 thin + nginx? - 2

      我的瘦服务器配置了nginx,我的ROR应用程序正在它们上运行。在我发布代码更新时运行thinrestart会给我的应用程序带来一些停机时间。我试图弄清楚如何优雅地重启正在运行的Thin实例,但找不到好的解决方案。有没有人能做到这一点? 最佳答案 #Restartjustthethinserverdescribedbythatconfigsudothin-C/etc/thin/mysite.ymlrestartNginx将继续运行并代理请求。如果您将Nginx设置为使用多个上游服务器,例如server{listen80;server

    8. ruby-on-rails - Rails 应用程序中的 Rails : How are you using application_controller. rb 是新手吗? - 2

      刚入门rails,开始慢慢理解。有人可以解释或给我一些关于在application_controller中编码的好处或时间和原因的想法吗?有哪些用例。您如何为Rails应用程序使用应用程序Controller?我不想在那里放太多代码,因为据我了解,每个请求都会调用此Controller。这是真的? 最佳答案 ApplicationController实际上是您应用程序中的每个其他Controller都将从中继承的类(尽管这不是强制性的)。我同意不要用太多代码弄乱它并保持干净整洁的态度,尽管在某些情况下ApplicationContr

    9. ruby-on-rails - form_for 中不在模型中的自定义字段 - 2

      我想向我的Controller传递一个参数,它是一个简单的复选框,但我不知道如何在模型的form_for中引入它,这是我的观点:{:id=>'go_finance'}do|f|%>Transferirde:para:Entrada:"input",:placeholder=>"Quantofoiganho?"%>Saída:"output",:placeholder=>"Quantofoigasto?"%>Nota:我想做一个额外的复选框,但我该怎么做,模型中没有一个对象,而是一个要检查的对象,以便在Controller中创建一个ifelse,如果没有检查,请帮助我,非常感谢,谢谢

    10. ruby - rspec 需要 .rspec 文件中的 spec_helper - 2

      我注意到像bundler这样的项目在每个specfile中执行requirespec_helper我还注意到rspec使用选项--require,它允许您在引导rspec时要求一个文件。您还可以将其添加到.rspec文件中,因此只要您运行不带参数的rspec就会添加它。使用上述方法有什么缺点可以解释为什么像bundler这样的项目选择在每个规范文件中都需要spec_helper吗? 最佳答案 我不在Bundler上工作,所以我不能直接谈论他们的做法。并非所有项目都checkin.rspec文件。原因是这个文件,通常按照当前的惯例,只

    随机推荐