多线程的学习2

继续了解多线程,多线程的通信方式我感觉主要就是全局变量,而且这个使用起来感觉也最方便,也许我的理解可能有误。

多线程的通信方式:

1、全局变量

全局变量在使用时通常会加上volatile声明(防止编译器对此变量进行优化),还有在使用中如果需要对这个全局变量进行操作,最好加上互斥量

2、message消息机制

常用的message通信的接口主要有:PostMessage和PostThreadMessage,前者是和向主窗口发送消息,后者是任意两个线程之间的通信接口。

PostMessage函数原型:
B00L PostMessage(HWND hWnd,UINT Msg,WPARAM wParam,LPARAM lParam);
 参数:
    hWnd:其窗口程序接收消息的窗口的句柄。可取有特定含义的两个值:
    HWND.BROADCAST:消息被寄送到系统的所有顶层窗口,包括无效或不可见的非自身拥有的窗口、被覆盖的窗口
 和弹出式窗口。消息不被寄送到子窗口。
    NULL:此函数的操作和调用参数dwThread设置为当前线程的标识符PostThreadMessage函数一样。
    Msg:指定被寄送的消息。
    wParam:指定附加的消息特定的信息。
    IParam:指定附加的消息特定的信息。
    返回值:如果函数调用成功,返回非零值:如果函数调用失败,返回值是零。
MS还提供了SendMessage方法进行消息间通讯,SendMessage(),他和PostMessage的区别是:
SendMessage是同步的,而PostMessage是异步的。SendMessage必须等发送的消息执行之后,才返回。
PostThreadMessage函数原型:
BOOL PostThreadMessage(DWORD idThread,UINT Msg,WPARAM wParam, LPARAM lParam);
参数除了ThreadId之外,基本和PostMessage相同。
目标线程通过GetMessage()方法来接受消息。
注:使用这个方法时,目标线程必须已经有自己的消息队列。否则会返回ERROR_INVALID_THREAD_ID错误。可以用
PeekMessage()给线程创建消息队列。
PostThreadMessage可以用于线程之间的异步通讯,因为它不用等待调用者返回,这也许是线程通讯中最简单的一种方法了。但是要注意以下问题。
1 .PostThreadMessage有时会失败,报1444错误(Invalid thread identifier. )其实这不一定是线程不存在的原因,也有可能是线程不存在消息队列(message queue)造成的。事实上,并不是每个thread都有message queue,那如何让thread具有呢?答案是,至少调用message相关的function一次,比如GetMessage,PeekMessage。
2.如果是post动态分配的memory给另外一个thread,要注意内存的正确释放。
3.PostThreadMessage不能够post WM_COPYDATE之类的同步消息,否则会报错
4.最好不要使用PostThreadMessage post message给一个窗口,使用PostMessage替代。

3、CEvent对象

CEvent为MFC中的一个对象,可以通过对CEvent的触发状态进行改变,从而实现线程间的通信和同步,这个主要是实现线程直接同步的一种方法。

线程之间的同步

线程之间的同步有多种方式:临界区(CCriticalSection)、事件(CEvent)、互斥量(CMutex)、信号量(CSemaphore),这些同步方式使得各个线程可以协调工作,程序运行起来更安全。

1、临界区
临界区是保证在某一个时间只有一个线程可以访问数据的方法。
临界区对应一个CcriticalSection对象,当线程需要访问保护数据时,调用临界区对象的lock成员函数,对数据进行保护,防止其他线程访问函数,当执行完操作后,在使用临界区的unlock成员函数,退出保护。
下面是一个简单的例子:

#include "afxmt.h"
int array[10],destarray[10];
CCriticalSection Section;
UINT WriteThread(LPVOID param)
{
 Section.Lock();
 for(int x=0;x<10;x++)
  array[x]=x;
 Section.Unlock();
}
UINT ReadThread(LPVOID param)
{
 Section.Lock();
 For(int x=0;x<10;x++)
  Destarray[x]=array[x];
 Section.Unlock();
}

2、互斥量
互斥量和临界区很相似,它不仅可以在同一应用程序的线程间实现同步,还可以在不同的进程间实现同步,从而实现资源的安全共享,在同一进城中建议使用临界区,因为相对而言临界区更节省系统资源,效率更高。
使用互斥对象时,必须创建一个CSingleLock或CMultiLock对象,用于实际的访问控制

3、信号量
信号量的用法和互斥量的用法很相似,不同的是它可以同一时刻允许多个线程访问同一个资源,创建一个信号量需要用CSemaphore类声明一个对象,一旦创建一个了一个信号量对象,就可以用它来对资源的访问进行控制。
首先要创建CSingleLock或CmltiLock对象,然后用创建的对象的lock函数减少这个信号量的计数值unlock反之。
下面是一段示例代码:

#include <windows.h>  
#include <process.h>  
using namespace  std;  
void semaphoreTest(void *ptr)  
{  
    int flag = *(int *)ptr;  
    HANDLE semaphore = CreateSemaphore(NULL, 2, 2, (LPCWSTR)"streamSemaphore");  
    WaitForSingleObject(semaphore, INFINITE);  
    。。。
    ReleaseSemaphore(semaphore, 1, NULL);  
    CloseHandle(semaphore);  
}   
int main()  
{  
    int flag[] = {1, 2, 3};  
    for (int i = 0; i < 3; ++i)  
    {  
        _beginthread(semaphoreTest, 0, &flag[i]);  
    }
    sleep(INFINITE);           
} 

猜你喜欢

转载自blog.csdn.net/bloke_come/article/details/73189432