MFC多线程编程

来源:互联网 发布:k8网络分销平台 编辑:程序博客网 时间:2024/06/16 21:04

MFC多线程开发

线程创建函数

HANDLE CreateThread(

LPSECURITY_ATTRIBUTESlpThreadAttribute,  //指向SECURITY_ATTRIBUTES结构体的指针

DWORD dwStackSize,                       //设置线程初始栈的大小(页面大小的整数//倍)默认为调用该函数的线程相同的栈空间大小

LPTHREAD_START_ROUTINElpStartAddress,   //新线程入口函数的指针

LPVOID lpParameter,                      //命令行参数

DWORD dwCreationFlags,                   //设置控制线程创建后状态的标记

LPDWORD lpThreadId                       //接收线程ID

);

CloseHandle()

当不需要线程句柄时,将其关闭,让这个线程内核对象的引用减1,当引用计数为0时,系统释放该线程内核对象

线程由线程内核对象和线程栈组成,在创建它的进程环境中运行,可随意访问

进程内核对象,进程地址空间

 

DWORD WINAPI ThreadProc(LPVOID lpParameter);

在程序定义一个函数作为新线程的入口函数。函数名任意,但函数类型声明格式固定

 

Void Sleep(DWORD dwMilliseconds);

线程同步

利用互斥对象实现线程同步

HANDLE CreateMutex(     //mutex互斥内核对象,当前拥有互斥对象的线程ID

                                           //和指明拥有的次数的计数器

LPSECURITY_ATTRIBUTESlpMutexAttributes,

BOOL bInitialOwner,                       //指定互斥对象初始的拥有者

LPCTSTR lpName                           //名称

)

BOOL ReleaseMutex(HANDLE hMutex);   //释放该对象的所有权,使其处于已通知状态实为//递减计数器的值

DWORD WaitForSingleObject(HANDLEhHandle,DWORD dwMilliseconds);

主动请求共享对象的使用权,dwmiliseconds为等待时间指定对象为有信号状态返回

返回值 WAIT_OBJECT_0  WAIT_TIMEOUT WAIT_ABANDONED

对于互斥对象,谁拥有谁释放

操作系统一旦发现线程已终止,自动将其拥有的互斥对象的线程ID设为0,计数器归0

保证应用程序只有一个实例(进程吗??)运行

GetLastError()函数  ERROR_ALREADY_EXISTS表明先前已经创建了这个命名的互斥对象

线程的时间片

很久以前,接触一个项目,看到一个while(1)死循环,但又发现程序经常跳出while去执行另外一段程序,甚为疑虑。其实,在多线程模式下,这是可能的,下面的程序就不会一直陷入mainwhile(1)循环:

1.  #include<stdio.h>

2. #include <windows.h>

3. DWORD WINAPI ThreadFun(LPVOID pM)

4.  {

5.     while(1) 

6.      { 

7.         printf("thread\n"); 

8.          exit(0); 

9.     } 

10.   

11.    return 0; 

12. 

13.  

14. int main() 

15.

16.     HANDLE handle = CreateThread(NULL,0, ThreadFun, NULL, 0, NULL); 

17.    CloseHandle(handle); 

18.   

19.    while(1) 

20.     { 

21.        printf("mainthread\n"); 

22.     } 

23.  

24.     return 0; 

25.

运行结果如下:

解释:系统为每个线程都分配有一定时间的时间片,当主线程一直在循环时,如果时间片的时间到了,系统会选择执行其他的线程。当主线程暂停时,系统由执行其他线程返回到执行主线程时是从哪里进入的,是重新又main进入,还是从暂停处进入?当然是暂停处。

 

exit(0) 退出进程,如果不退出,当线程ThreadFun时间片到了会继续执行主线程的循环

线程同步

*互斥对象实现线程同步*/#include <windows.h>#include <iostream>using namespace std;DWORD WINAPI Fun1Proc(LPVOID lpParameter //thread data);DWORD WINAPI Fun2Proc(LPVOID lpParameter //thread data);int index=0;int tickets=100;HANDLE hMutex;void main(){HANDLE hThread1;HANDLE hThread2;hThread1=CreateThread(NULL,0,Fun1Proc,NULL,0,NULL);hThread2=CreateThread(NULL,0,Fun2Proc,NULL,0,NULL);CloseHandle(hThread1);CloseHandle(hThread2);hMutex=CreateMutex(NULL,FALSE,NULL);if(hMutex){if(ERROR_ALREADY_EXISTS==GetLastError()){cout<<"only one instance can run!"<<endl;return;}}WaitForSingleObject(hMutex,INFINITE);ReleaseMutex(hMutex);ReleaseMutex(hMutex);//while(index++<1000)//{//cout<<"main thread is running"<<endl;////Sleep(10);//}//Sleep(10);Sleep(40000);}DWORD WINAPI Fun1Proc(LPVOID lpParameter){/*while(index++<1000)cout<<"thread1 is running"<<endl;*/while(TRUE){WaitForSingleObject(hMutex,INFINITE);if(tickets>0){Sleep(1);cout<<"thread1 sell ticket:"<<tickets--<<endl;}elsebreak;ReleaseMutex(hMutex);}return 0;}DWORD WINAPI Fun2Proc(LPVOID lpParameter){/*while(index++<1000)cout<<"thread2 is running"<<endl;*/while(TRUE){WaitForSingleObject(hMutex,INFINITE);if(tickets>0){Sleep(1);cout<<"thread2 sell ticket:"<<tickets--<<endl;}elsebreak;ReleaseMutex(hMutex);}return 0;}


运行结果:




利用事件对象实现线程同步

创建事件对象:

HANDLE CreateEvent(     //事件对象

    LPSECURITY_ATTRIBUTESlpEventAyyributes,//NULL,默认安全性

    BOOLbManualReset,      //真为人工重置事件对象或自动重置事件对象

    BOOLbInitialState,     //初始状态,真为有信号,假为无信号

    LPCTSTRlpName          //事件对象名称

);

设置事件对象状态

BOOL SetEvent(HANDLE hEvent);

重置事件对象状态

BOOL ResetEvent(HANDLE hEvent);

事件对象三个成员:使用计数、指明事件是自动重置还是手动重置、指明事件是通知状态还是未通知状态。

人工重置事件对象:得到通知,等待该事件对象的所有线程变为可调度线程,当线程等待到对象的所有权后,调用ResetEvent函数手动设置为无信号状态

自动重置事件对象:得到通知,只有一个等待线程变为可调度线程,系统自动将对象设置为无信号状态


*事件对象实现线程同步*/#include <windows.h>#include <iostream>using namespace std;DWORD WINAPI Fun1Proc(LPVOID lpParameter //thread data);DWORD WINAPI Fun2Proc(LPVOID lpParameter //thread data);//int index=0;int tickets=100;HANDLE hEvent;void main(){HANDLE hThread1;HANDLE hThread2;hEvent=CreateEvent(NULL,FALSE,FALSE,NULL);//hEvent=CreateEvent(NULL,TRUE,FALSE,NULL);SetEvent(hEvent);hThread1=CreateThread(NULL,0,Fun1Proc,NULL,0,NULL);hThread2=CreateThread(NULL,0,Fun2Proc,NULL,0,NULL);CloseHandle(hThread1);CloseHandle(hThread2);/*if(hEvent){if(ERROR_ALREADY_EXISTS==GetLastError()){cout<<"only one instance can run!"<<endl;return;}}*///WaitForSingleObject(hEvent,INFINITE);//ReleaseMutex(hEvent);//ReleaseMutex(hEvent);//while(index++<1000)//{//cout<<"main thread is running"<<endl;////Sleep(10);//}//Sleep(10);Sleep(40000);CloseHandle(hEvent);}DWORD WINAPI Fun1Proc(LPVOID lpParameter){/*while(index++<1000)cout<<"thread1 is running"<<endl;*/while(TRUE){WaitForSingleObject(hEvent,INFINITE);//ResetEvent(hEvent);if(tickets>0){Sleep(1);cout<<"thread1 sell ticket:"<<tickets--<<endl;SetEvent(hEvent);}else{break;SetEvent(hEvent);}//ReleaseMutex(hEvent);}return 0;}DWORD WINAPI Fun2Proc(LPVOID lpParameter){/*while(index++<1000)cout<<"thread2 is running"<<endl;*/while(TRUE){WaitForSingleObject(hEvent,INFINITE);//ResetEvent(hEvent);if(tickets>0){Sleep(1);cout<<"thread2 sell ticket:"<<tickets--<<endl;SetEvent(hEvent);}else{break;SetEvent(hEvent);}//ReleaseMutex(hEvent);}return 0;}


关键代码段(临界区)实现线程同步

在代码能够执行前,它必须独占对某些资源的访问权,类似于电话亭

初始化临界区对象

Void InitializeCriticalSection(LPCRITCAL_SECTIONlpCriticalSection);out类型参数

等待获得所有权?使用权

EnterCriticalSection()

释放临界区对象的所有权

LeaveCriticalSection()

删除指定临界区对象

DeleteCriticalSection()

 

线程死锁

线程1获得临界区对象a的所有权的同时请求临界区对象b的所有权,而同时线程2获得临界区对象b的所有权后申请临界区对象a的所有权,造成线程死锁

互斥对象和事件对象是内核对象,速度缓慢,但可在多个进程中的各个线程中进行同步;

临界区对象工作在用户态,容易进入死锁,但首选关键代码段



/*关键代码段实现线程同步*/#include <windows.h>#include <iostream>using namespace std;DWORD WINAPI Fun1Proc(LPVOID lpParameter //thread data);DWORD WINAPI Fun2Proc(LPVOID lpParameter //thread data);//int index=0;int tickets=100;CRITICAL_SECTION cs;void main(){HANDLE hThread1;HANDLE hThread2;//hEvent=CreateEvent(NULL,TRUE,FALSE,NULL);//SetEvent(hEvent);hThread1=CreateThread(NULL,0,Fun1Proc,NULL,0,NULL);hThread2=CreateThread(NULL,0,Fun2Proc,NULL,0,NULL);CloseHandle(hThread1);CloseHandle(hThread2);/*if(hEvent){if(ERROR_ALREADY_EXISTS==GetLastError()){cout<<"only one instance can run!"<<endl;return;}}*///WaitForSingleObject(hEvent,INFINITE);//ReleaseMutex(hEvent);//ReleaseMutex(hEvent);//while(index++<1000)//{//cout<<"main thread is running"<<endl;////Sleep(10);//}//Sleep(10);InitializeCriticalSection(&cs);Sleep(40000);DeleteCriticalSection(&cs);//CloseHandle(hEvent);}DWORD WINAPI Fun1Proc(LPVOID lpParameter){/*while(index++<1000)cout<<"thread1 is running"<<endl;*/while(TRUE){//WaitForSingleObject(hEvent,INFINITE);//ResetEvent(hEvent);EnterCriticalSection(&cs);Sleep(1);if(tickets>0){Sleep(1);cout<<"thread1 sell ticket:"<<tickets--<<endl;//SetEvent(hEvent);LeaveCriticalSection(&cs);}else{break;//SetEvent(hEvent);LeaveCriticalSection(&cs);}//ReleaseMutex(hEvent);}return 0;}DWORD WINAPI Fun2Proc(LPVOID lpParameter){/*while(index++<1000)cout<<"thread2 is running"<<endl;*/while(TRUE){//WaitForSingleObject(hEvent,INFINITE);//ResetEvent(hEvent);EnterCriticalSection(&cs);Sleep(1);if(tickets>0){Sleep(1);cout<<"thread2 sell ticket:"<<tickets--<<endl;//SetEvent(hEvent);LeaveCriticalSection(&cs);}else{break;//SetEvent(hEvent);LeaveCriticalSection(&cs);}//ReleaseMutex(hEvent);}return 0;}