当前位置: 首页 > news >正文

大型网站设计方案wordpress多文件下载插件

大型网站设计方案,wordpress多文件下载插件,音乐网站禁止做浅度链接,html5网站模板 医院本篇解释了为什么创建新线程的时候使用_beginThread比使用CreateThread更为安全这一问题。 C/C库的历史问题#xff1a; 标准的C运行库(C Runtime Class, CRT)#xff0c;是在1970年发明的#xff0c;那个时候操作系统上还没有线程的概念#xff0c;理所当然地#xff0c;… 本篇解释了为什么创建新线程的时候使用_beginThread比使用CreateThread更为安全这一问题。 C/C库的历史问题 标准的C运行库(C Runtime Class, CRT)是在1970年发明的那个时候操作系统上还没有线程的概念理所当然地最初的C运行时库是线程不安全的。下面给出一个例子 标准C运行库有一个全局变量errno。有一些函数会在出错的时候设置这个变量。 如果A线程中需要errno来作为参数进行某些判断而errno是全局变量对于A线程来说errno是不安全的因为errno随时都可能被别的线程修改。 与此类似在多线程环境中会出现问题的C/C运行库变量和函数还有很多。 解决思路 创建一个数据结构并将这个数据结构与使用了C/C运行库函数的每个线程关联。然后在调用C/C运行库的时候那些函数必须知道去查找主调线程的数据块(而不是像之前一样对全局变量进行操作)从而避免影响到其它线程。 这个解决思路包含了两方面的内容 一方面是需要一块与线程关联的数据块来对可能出现问题的C/C运行库变量进行存储 另一方面是需要修改原来的C/C运行库函数使其在调用时调用数据块中的内容而不是全局变量并为其增加一些多线程的安全机制。 _beginthreadex 根据上面的解决思路系统在创建新线程的时候应该为之关联一个数据块。但一开始被开发出来的CreateThread函数是没有这样的功能的它只负责创建线程并不负责C/C运行库代码在这个线程中运行的安全性。 _beginthreadex在内部调用了CreateThread在调用之前_beginthreadex做了很多的工作从而使得它比CreateThread更安全。 通常建议使用_beginthreadex函数而不是CreateThread函数这使得线程中的代码不需要考虑C/C代码的线程安全性除非你清楚地知道在新的线程中不会调用到线程不安全的C/C代码这时候放心地使用CreateThread也无可厚非(实际上这种情况很难判定)。_beginthreadex保证了某些C/C运行库代码的线程安全性而CreateThread没有对这些特殊的C/C代码做出保证这里再次强调这两个函数的区别。不要含糊地认为CreateThread的设计存在缺陷CreateThread的功能并不专门针对于C/C运行库理所当然不必为其多线程安全性而负责。 下面先给出_beginthreadex的伪代码之后对其内部操作做出解释 unsigned long  __cdecl _beginthreadex (       void  *psa,       unsigned cbStack,       unsigned (__stdcall * pfnStartAddr) ( void  *),       void  * pvParam,       unsigned fdwCreate,       unsigned *pdwThreadID) {       _ptiddata ptd;         // 指向线程数据块_tiddata的指针       unsigned long  thdl;    // Threads handle       // 为线程数据块_tiddata分配空间这块控件是从C/C运行库的堆上分配的       if  ((ptd calloccrt(1, sizeof ( struct  tiddata))) NULL)           goto  errorreturn;       // 初始化线程数据块       initptd(ptd);       // Save the desired thread function and the parameter       // we want it to get in the data block       ptd-_initaddr ( void  *) pfnStartAddr;       ptd-_initarg pvParam;       // 调用CreateThread创建新线程注意其线程函数并不是pfnStartAddr       // 参数也不是pvParam而是线程数据块的指针ptdpfnStartAddr和       // pvParam也被存储在这个数据块里。       thdl (unsigned long ) CreateThread(psa, cbStack,_threadstartex,                       ( PVOID ) ptd, fdwCreate, pdwThreadID);       if  (thdl NULL) {           // Thread couldnt be created, cleanup and return failure           goto  error_return;       }       // Create created OK, return the handle       return (thdl); error_return:       // Error: data block or thread couldnt be created       _free_crt(ptd);       return ((unsigned long )0L); } 对于_beginthreadex函数需要注意以下几点 1.每个线程都有自己专有的_tiddata内存块它们是从C/C运行库的堆中分配的。 2._beginthreadex在内部调用了CreateThread但传入的线程函数地址是_threadstartex而不是pfnStartAddr。 3._beginthreadex函数体内做的工作只是创建了用于线程关联的_tiddata数据块但关联这个数据块的任务是由_threadstartex函数在新线程被创建之初完成的。 下面给出_threadstartex函数的伪代码并对其做出解释 static  unsigned long  WINAPI _threadstartex ( void * ptd) {      // 将_tiddata数据块的指针与这个线程关联起来可以调用_getptd()函数来得到这个指针      TlsSetValue(__tlsindex, ptd);      // Save this thread ID in the tiddata block      ((_ptiddata) ptd)-_tid GetCurrentThreadId();            // Initialize floating-point support (code not shown)      // 调用_callthreadstartex      _callthreadstartex();      // We never get here, the thread dies in this function      return (0L); } static  void  _callthreadstartex( void ) {      // 之前已经将_tiddata与线程关联所以可以调用_getptd得到线程的_tiddata数据块      _ptiddata ptd _getptd();      // Wrap desired thread function in SEH frame to      // handle runtime errors and signal support      // 将要执行的线程函数以SEH帧包裹起来从而为运行时错误和signal函数提供支持      __try  {          // 调用传给_beginthreadex的线程函数pfnStartAddr线程函数执行结束之后          // 返回代码将传递给_endthreadex          _endthreadex(            ( (unsigned (WINAPI *)( void  *))(((_ptiddata)ptd)-_initaddr) )                ( ((_ptiddata)ptd)-_initarg ) ) ;      }      __except(_XcptFilter(GetExceptionCode(), GetExceptionInformation()){          // The C-Runtimes exception handler deals with runtime errors          // and signal support, we should never get it here.          _exit(GetExceptionCode());      } } 对于_threadstartex函数需要注意以下几点 1.新的线程将首先执行RtlUserThreadStart函数之后将跳转到_threadstartex函数。这点可以结合之前对线程启动内幕的讨论来理解。 2._threadstartex唯一的参数就是_tiddata内存块的地址_tiddata内存块中包括了pfnStartAddr和pvParam。 3.TlsSetValue是一个操作系统函数它将一个值与主调线程关联起来。这就是所谓的线程局部存储(Thread Local Storage, TLS)。之后的章节中也许会对这个做出专门的讨论。 4.在无参辅助函数_callthreadstartex中有一个SEH帧它将预期要执行的线程函数(pfnStartAddr)包围起来。这个帧处理着与运行库有关的许多事情——比如运行时错误(如抛出未被捕捉的C异常)——和C运行库的signal函数。这一点相当重要如果调用CreateThread函数创建了一个线程然后调用C/C的signal函数那么signal函数将不能工作。 5.注意_callthreadstartex不是简单地返回到_threadstartex继而到RtlUserThreadStart如果是那样的话线程会终止运行其退出代码也会被正确设置但是线程的_tiddata内存块不会被正确销毁。所以_callthreadstartex在线程函数退出后调用了_endthreadex函数。 下面给出_endthreadex函数的伪代码并对其做出解释 void  __cdecl _endthreadex (unsigned retcode) {       // Cleanup floating-point support (code not shown)       // Get the address of this threads tiddata block       _ptiddata ptd _getptd();       // Free the tiddata block       _freeptd(ptd);       // Terminate the thread       ExitThread(retcode); } 从代码可以看出_endthreadex函数所做的主要事情就是释放_tiddata数据块的内存之后它会直接调用ExitThread来执行线程退出的操作。这意味着线程不会返回到RtlUserThreadStart函数中而是直接退出。CreateThread没有这么复杂的操作线程函数会直接返回RtlUserThreadStart函数之后ExitThread被调用来终结线程。 现在应该理解了C/C运行库函数为什么要为每一个线程准备一个独立的数据块而且应该理解了_beginthreadex如何分配和初始化此数据块并将它与数据库关联起来。另外还理解了_endthreadex函数在线程终止的时候是如何释放该数据块的。 一旦这个数据块被初始化并与进程相关联线程调用的任何需要多线程实例数据的C/C运行库函数都可以轻易获取主调线程的数据块的地址(通过TlsGetValue)并操纵线程的数据。这对函数来数是没有问题的。但是对于erron之类的全局变量它又是如何工作的呢 errno是在标准C头文件中定义的如下所示 _CRTIMP extern  int  * __cdecl _errno( void ); #define errno (*_errno()) int * __cdecl _errno( void ) {      _ptiddata ptd _getptd_noexit();      if (!ptd){          return  ErrnoNoMem;      } else  {          return  (ptd-_terrno);      } 这样一来任何时候引用errno实际都是在调用内部的C/C运行库函数_errno。该函数将地址返回给与主调线程关联的数据块中的errno数据成员。 C/C运行库还围绕特定的函数放置了同步对象(synchronization primitive)。例如如果两个线程同时调用malloc堆就会破坏C/C运行库函数不允许两个线程同时从内存堆中分配内存。具体的办法是让第二个线程等待直至第一个线程从malloc函数返回。然后才允许第二个线程进入。 就像我们期望的一样C/C运行库的启动代码为应用程序的主线程分配并初始化了一个数据块。这样一来主线程就可以安全地调用任何C/C运行库函数。当主线程从其入口点函数返回的时候C/C运行库函数会释放关联的数据块。此外启动代码设置了正确的结构化异常处理代码使主线程能成功调用C/C运行库的signal函数。 用_beginthreadex而不要用CreateThread创建线程 假如调用CreateThread而不调用_beginthreadex会发生什么呢当一个线程调用一个需要_tiddata结构的C/C运行库函数时会发生下面的情况。(大多数C/C运行库函数都是线程安全的不需要这个结构)首先C/C运行库函数尝试取得线程数据块的地址(通过TlsGetValue)。如果返回的值是NULL,表明主调线程没有与之关联的_tiddata块。在这个时候C/C运行库函数会为主调线程分配并初始化一个_tiddata块。然后这个块会与线程关联(通过TlsGetValue)而且只要线程还在运行这个块就会一直存在并且与线程关联。现在C/C运行库函数可以使用线程的_tiddata块以后调用的任何C/C运行库函数也都可以使用。 当然这是相当诱人的因为线程(几乎)可以顺畅运行。但事实上问题还是有的。第一个问题是假如线程使用了C/C的signal函数则整个进程都将终止因为结构化异常处理帧没有就绪。第二个问题是假如线程不是通过调用_endthreadex来终止的数据块就不能被销毁从而导致内存泄漏。(对于一个用CreateThread函数创建的线程谁会调用_endthreadex呢) 转载自http://www.cnblogs.com/zuibunan/archive/2012/07/20/2600900.html
http://www.dnsts.com.cn/news/239569.html

相关文章:

  • 深圳都信建设监理有限公司网站网站虚拟空间多少钱
  • 电商网站如何做多语言架构羽毛球赛事介绍
  • 安阳网站建设银行网站入口
  • 可以做空股票的网站做网站都用到哪些软件
  • 机票网站建设公司wordpress多站
  • 网站源代码上传阿里企业邮箱电话
  • 网站流量分析方法提供深圳网站制作公司
  • 我的世界做图的网站网站相互推广怎么做
  • 三拼域名做网站长不长道滘镇网站建设公司
  • 鹤壁网站制作什么是网站上线检测
  • 英文网站设计广东如何进行网站制作排名
  • 做网站市场报价步登顶电商推广文案
  • 单页面网站可以做自适应网站吗教案怎么写模板
  • 天津市建设工程合同备网站怎么在后台设计网站
  • 什么网站免费做游戏wordpress布局
  • 保定网站设计公司排名wordpress 开发文档下载
  • 国外档案网站建设dedecms本地可以更换网站模板出现网站模板不存在
  • 网站开发技术背景介绍钉子wordpress主题
  • 电子商务网站建设的认识的心得wordpress 文章的id
  • 网站开发时间一般是东莞58同城招聘网最新招聘信息
  • 网站模板中心 网站推荐株洲网站建设报价
  • 网站内链案例汕头百度网络推广
  • 做网站一个月可以赚多少宁波网站设计公司有几家
  • 做企业网站怎么接活机票网站开发
  • 海淀区城市建设档案馆网站免费建设一个网站
  • 外贸石材网站怎样做网站管理
  • php如何做音乐网站中国建设教育协会是什么网站
  • 多语言外贸网站设计wordpress ping通告
  • 商务网站建设的调研流程企业网站的意思
  • 计算机网站建设好不好企业邮箱如何查询