zoukankan      html  css  js  c++  java
  • 《Win32多线程程序设计》学习笔记 第14章 建造DLL

    DLL的通告消息Notifications

     任何时候,当一个进程载入或卸载一个dll时,dllmain会被调用。线程也是一样的。当一个进程开始执行时,它所用到的每个dll的dllmain都会被系统调用之,并获得一个DLL_PROCESS_ATTACH消息。如果是线程开始执行,进程所用到的每一个dll的dllmain也都会被系统调用之,并获得DLL_THREAD_ATTACH消息。

    BOOL WINAPI DllMain(
      HINSTANCE hinstDLL,   
    //这个dll的module handle
      DWORD fdwReason,      //dllmain被调用的原因。可能是以下之一:DLL_PROCESS_ATTACH;DLL_THREAD_ATTACH;DLL_THREAD_DETACH;DLL_PROCESS_DETACH
      LPVOID lpvReserved    //提供更多信息补充fdwReason,如果fdwReason是DLL_PROCESS_ATTACH,那么lpvReserved 为NULL表示dll是被loadlibrary载入的,否则是隐式载入
    );

    返回值:如果fdwReason是DLL_PROCESS_ATTACH,那么dllmain应该在成功时传回TRUE,在失败时传回FALSE。如果不是,那么返回值被忽略。

    Win32 中,每个程序的第一个线程调用dllmain时,是以dll_process_attach调用,后续的线程才是以dll_thread_attach调用。dllmain是在新线程的context中被调用。因为你需要一个context,才能够使用线程局部存储(TLS)。

    如果要加载的dll很多时,由于dllmain的调用,会加大很多负担,所以可以使用如下函数禁用不需要通告消息的dlls

    BOOL WINAPI DisableThreadLibraryCalls(
      HMODULE hModule  
    //dll的module handle
    );

    返回:如果成功,返回TRUE。如果所指定的dll使用了线程局部存储,这个函数调用一定会失败。

    当一个dll被loadlibrary或者loadlibraryex动态载入时,dllmain不会收到任何正在执行的线程的dll_THREAD_ATTACH通告消息,但有一个线程除外,就是调用loadlibrary的那个。

    如果DllMain收到DLL_PROCESS_ATTACH时因不能正确初始化而传回FALSE,DllMain还是会收到DLL_PROCESS_DETACH

    • 如果进程调用LoadLibrary时,有一个以上的线程正在运行,那么DLL_THREAD_ATTACH不会针对每一个线程送出。只有调用LoadLibrary的那个线程才发出。
    • DllMain不接受第一个线程的DLL_THREAD_ATTACH,而已DLL_PROCESS_ATTACH代之
    • DllMain不接受任何因TerminateThread而结束之线程的DLL_TREAD_DETACH通告消息。如果程序调用exit(1)或exitProcess结束自己,这种情况会发生。

     DLL进入点的依序执行(Serialization)特性

     在一个进程之中,一次只能有一个线程执行一个DLL的DllMain函数。每个线程依次调用每个附着的Dlls的DllMain函数。

    MFC中的Dll通告消息

     一个使用MFC的Dll,拥有它自己的CWinThread对象,当Dll接受到DLL_PROCESS_ATTACH时,MFC会调用CWinThread::InitInstance。当Dll收到DLL_PROCESS_DETACH时,调用CWinThread::ExitInstance函数,另外2个消息,没有虚函数被调用。

     喂食给Worker线程

     我们在一个worker线程中调用GetMessage之类的函数时,系统就会给该线程产生一个消息队列。这样我们就可以使用消息来和这个worker线程通信了。

    线程局部存储(TLS)

     TLS是一种机制,通过它,线程可以持有一个指针,指向它自己的一份数据结构拷贝。MFC使用TLS来追踪每个线程所使用的GDI对象和USER对象。

    线程局部存储的运作方式是,每个线程有一个由4字节槽所组成的数组。 这个数组保证至少有TLS_MINIMUM_AVAILABLE个槽在其中。至少64.每个槽可被指定放置特殊结构。

    所有被配置的内存 ,甚至是dll所配置的内存,都是在调用端进程的context 中配置的。你的Dll会获得每一个调用端进程的所有全局变量的一份拷贝。

     TLS的使用起始于TlsAlloc。TlsAlloc()在TLS数组中配置一个槽,并传回其数组索引。每个线程可以自己拥有一份槽内容的拷贝,但是所有拷贝都必须使用同一槽索引。

    DWORD WINAPI TlsAlloc(void);

    返回值:如果成功,传回一个TLS数组中的一个槽。

    BOOL WINAPI TlsSetValue(
        DWORD dwTlsIndex,    
    //由TlsAlloc传回的TLS槽索引
        LPVOID lpTlsValue    //要储存到槽中的数据值
    );

    返回值:成功返回TRUE
    LPVOID WINAPI TlsGetValue(
      __in          DWORD dwTlsIndex   
    //槽索引
    );

    返回值:成功,传回存在槽中的值。失败返回0

    BOOL WINAPI TlsFree(
      __in          DWORD dwTlsIndex
    );

    //成功返回TRUE

    一个TLS索引只在同一个进程中才有意义。

     _declspec(thread)

     将一个变量或结构声明为“具有线程局部性”。

    • 如果一个对象又有构造函数或者析构函数,就不能够被声明为_declspec(thread).因此,你必须在线程启动时手动初始化对象。
    •  一个Dll如果使用了_declspec(thread),就没有办法被LoadLibrary载入。

     数据一致性

    •  不要使用全局变量,用来储存槽着除外
    • 不要是用静态变量。
    • 尽量使用TLS
    • 尽量使用你的堆栈
  • 相关阅读:
    asp.net c#中去掉最后一个字符和去掉第一个字母
    两个div并排
    VS.Net2005中使用本地化功能实现多语言的切换
    gridview嵌套DropDownList選定值[转]
    C# 获取系统时间
    NERDTree,好用的文件浏览器
    通过$.browser来判断浏览器
    vim 智能提示
    让vim显示函数列表
    vim中文乱码解决方法
  • 原文地址:https://www.cnblogs.com/kwliu/p/2195900.html
Copyright © 2011-2022 走看看