多线程二

一、线程间数据的一致性

volatile关键字:这个关键字表示这个变量有可能发生改变,每次需要的时候需要到内存中的获取(因为系统可能优化将常用的数据保存到寄存器中)。

1.排他锁定。

二、使用C Run-time Library

          早期的C Run-time Library有数个的全局变量和静态变量,对于多线程的程序来说,使用这个库可能造成冲突,因此后来又设计了一个多线程版的C Run-time Library,这样每个线程都有自己的局部变量取代全局变量和静态变量。这样一个版本用于单线程程序,一个版本用于多线程程序。

 注:在MFC中必须使用多线程版本的C Run-time Library,否则会造成链接错误。

         为了保证多线程情况下的安全,Run-time Library需要做一些薄记功夫,用于保证为每个线程配置一块新内存,作为线程的局部变量用。这样,CreateThread()有一个名为_beginthreadex()函数,用于负责薄记工作。

       _beginthreadex()函数和CreateThread函数的参数相同,只是将参数净化了,即WIN32自定义类型转化为C的参数类型。_beginthreadex()函数返回值和CreateThread()相同,但_beginthreadex()函数另外设立了errno和doserrno全局变量,用于_beginthreadex()函数调用失败的原因。而在_beginthreadex()函数中又调用了CreateThread()函数,因此我们还需要CloseHandle()函数来释放线程内核对象。

        还有一个用于结束线程的C Run-time Library函数:_endthreadex();  可以被任意线程的任意时候调用。

注:不要在一个“以_beginthreadex()启动的线程”中调用ExitThread(),因为这样C Run-time Library会没有机会释放为该线程而配置的资源。

下面的情况,你应该使用多线程版的C Run-time Library,并且使用_beginthreadex()和_endthreadex()函数:

       A)   在C程序中使用malloc()和free(),或是在C++程序中使用new 和delete.

       B)    嗲用stdio.h或io.h中声明的任何函数。

       C)   使用浮点变量或浮点运算函数

       D)   调用任何一个使用了静态缓冲区的runtime函数,如asctime()、strtok()或rand()。

为了清除C Run-time Library中的结构,对于以_beginthred()或_beginthreadex()来来产生新线程程序,可以通过使用下面两种技术来结束整个程序:

       A)   调用C Run-time Library的exit()函数。

       B)   从main()返回系统。

为什么应该避免使用_beginthread()函数?

_beginthread函数的原型: unsigned long _beginthread(

                                                   void (_cdecl *start_address)(void *), 

                                                    unsinged stack_size,

                                                    void *arglist);

这个函数返回一个线程句柄,但_beginthread返回后第一件事就是关闭自己的handle,因此这个句柄可能是不可用的,因此使用这个句柄会造成错误。并且这个函数和CrateThread函数的参数不一样,有些事情它无法办到,无法让线程产生于挂起状态。

结束线程:_endthread();这个函数无错误代码,无法使用GetExitCodeThread()获取。

三、使用C++

使用C方式的线程函数或者将线程函数设置静态的。如果是非静态的成员函数,其会有一个this指针,这样会造成this指针丢掉,造成错误。

四、MFC中的线程

     如果要在MFC程序中产生一个线程,而该线程将调用MFC函数或使用MFC的任何数据,那么你必须以AfcBeginThread()或CWinThread::CreateThread()来产生这些线程。

 

五、GDI和窗口管理

       在win32中维护着一个系统消息队列,对于每个GUI线程都有自己的专属消息队列,并不意味着每个窗口都有自己的消息队列。对于非GUI线程来说,建立起初没有消息队列,当第一次调用GDI函数时,系统才会为线程创建消息队列。

       所有传送给某一窗口的消息,都将由产生该窗口的线程负责处理。

       使用SendMessage函数将消息传给另一个线程所要做的工作:系统必须做一次上下文切换(context switch),切换到另一个线程,调用窗口函数,然后再做一次上下文切换,这个代价是比较大的,因此应该尽量将窗口保存在一个线程中。同一线程中将减少上下文切换过程。

       sendmessage正在被调用时,还能够处理由sendmessage产生的消息,但不能处理其他消息。当执行线程(接收SendMessage传过来消息的线程)调用一下函数时,等待的线程(调用Sendmessage的线程)将必须醒来:

      A) DialogBox()

      B) DialogBoxIndirect()

      C) DialogBoxIndirectParam()

      D) DialgBoxParam()

      E) GetMessage()

      F)  MessageBox()

      G)  PeekMessage()

我们可以通过调用ReplayMessage()函数来让等待的消除能够继续工作。

为了解决调用SendMessage函数造成等待线程一直等待的问题,有两个函数:

       A) SendMessageTimeout();  允许你指定一个时间,时间终了后不管对方怎样,SendMessageTimeout一定返回。对方挂了,也会自动返回。

       B) SendMessageCallback(); 该函数会理科返回,但其参数之一时一个函数地址,应该被以SendMessage()的方式调用起来。

 

线程间通信:

      A)PostThreadMessage();这个函数多了一个线程ID,这个函数将线程传送给另一个线程,当然另一个线程必须要有消息队列。

      B)全局变量

六、进程间通讯

  A)    WM_COPYDATA消息:可以将一个线程中的数据搬到另一个线程,不管两个线程是否在同一个进程中。

            使用方法: SendMessage(hwnd,WM_COPYDATA,(WPARAM)hwndSender,(LPARAM)&cds);其中cds必须是指向一个特定windows数据结构:

           typedef struct tagCOPYDATASTRUCT{

                     DWORD  dwData;     //一个用户自定义值,用于区分消息的类型。

                     DWORD  cbData;    //lpData数据大小

                      PVOID     lpData;     //一块数据,可以被传到接收端

            }COPYDATASTRUCT,*PCOPYDATASTRUCT;

          你只能使用SendMessage函数发送消息,因为你需要为传送的数据的缓冲区留时间进行处理。

          这种方式的缺点:效率不够到,没办法使用PostThreadMessage函数。

   B) 共享内存 (文件映射) 函数:CreateFileMapping,OpentFileMapping,MapViewOfFile,UnmapViewOfFile,CloseHandle.

   C)  Anonymous Pipes

   D) Named Pipes

   E)  Mailslots

   F)  OLE Automation

   G) DDE

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值