线程与进程:
一个应用程序会有多个进程,一个进程中会有多个线程。
进程是线程的容器,进程不执行具体操作
每个进程至少有一个线程,来执行地址空间中的代码
当创建一个进程时操作系统会自动创建这个进程的第一个线程也就是主线程,也就是执行mian函数和winmain函数的线程。此后主线程可以创建其它的线程。
每个进程有独立的内存空间,而线程与线程之间共享内存空间。
创建进程函数:CreateProcess
建立线程函数:AfxBeginThread
有两个原形:分别创建工作者线程(Worker Thread)处理后台和用户界面线程(User Interface Thread)
工作者线程的函数形式如下:(必须是全局函数)
UINT MyControllingFunction( LPVOID pParam);
创建一个线程:createThread
线程调用函数:DWORD WINAPI ThreadProc([in] LPVOID lpParameter);
线程暂停运行:VOID WINAPI Sleep(DWORD dwMilliseconds);
例: void main()
{
HANDLE hThread;
hThread=CreateThread(NULL,0,Fun1Proc,NULL,0,NULL);//创建线程
CloseHandle(hThread);//关闭线程的句柄,线程没关闭,只是对其引用不感兴趣而已
Sleep(10);
}
DWORD WINAPI Fun1Proc(LPVOID lpParameter)
{
cout<<"thread1 is running"<<endl;
return 0;
}
线程的同步:
1、 互斥对象:CreateMutex
互斥对象(mutex)属于内核对象,它能够确保线程拥有对单个资源的互斥访问权。
互斥对象包含一个使用数量,一个线程ID和一个计数器。(哪个线程拥有,ID就是哪个线程的ID)
ID用于标识系统中的哪个线程当前拥有互斥对象,计数器用于指明该线程拥有互斥对象的次数。
申请互斥对象:WaitForSingleObject
释放互斥对象: ReleaseMutex
注意:因为互斥对象里面有线程ID,只有对应的线程才能对其进行释放。还有申请多少次就得释放多少次(因为互斥对象里面有个计数器,记录被申请的次数)
当线程终止时还没有释放互斥对象时,操作系统会自动将其释放!这种情况可以由WaitForSingleObject的返回值来获得
例子: HANDLE hmutex;//定义互斥对象句柄
hmutex=CreateMutex(NULL,FALSE,NULL);//创建互斥对象
//为false表示主线程不拥有互斥对象
DWORD WINAPI Fun1Proc(LPVOID lpParameter)
WaitForSingleObject(hmutex,INFINITE);//得到互斥对象,时间为无限
if(tickets>0)
{ Sleep(1);
cout<<"thread1 sell tickets:"<<tickets--<<endl;
}
ReleaseMutex(hmutex);//释放互斥对象
判断程序重复运行:
hmutex=CreateMutex(NULL,FALSE,"tickets");//创建互斥对象
if(hmutex)
{
if(ERROR_ALREADY_EXISTS==GetLastError())
{
cout<<"程序已经运行!"<<endl;
}
}
二、事件对象:
CreateEvent :创建一个事件对象
ResetEvent:设置事件对象为非信号对象
SetEvemt:设置事件对象为有信号状态
事件对象也属于内核对象,包含一个使用计数,一个用于指明该事件是一个自动重置的事件还是一个人工重置的事件的布尔值,另一个用于指明该事件处于已通知状态还是未通知状态的布尔值。
有两种不同类型的事件对象。一种是人工重置的事件,另一种是自动重置的事件。当人工重置的事件得到通知时,等待该事件的所有线程均变为可调度线程。当一个自动重置的事件得到通知时,等待该事件的线程中只有一个线程变为可调度线程。(为了同步,则要设为自动重置)
三、关键代码段(CriticalSection)
InitiallizeCriticalSection:初始化一个临界区对象
EnterCriticalSession进入临界区对象
leaveCriticalSection:释放对临界区对象的所有权
deleteCriticalSection: 临界区对象不再使用后释放相关资源
各种方法比较:
n 互斥对象和事件对象属于内核对象,利用内核对象进行线程同步,速度较慢,但利用互斥对象和事件对象这样的内核对象,可以在多个进程中的各个线程间进行同步。
n 关键代码段是工作在用户方式下,同步速度较快,但在使用关键代码段时,很容易进入死锁状态,因为在等待进入关键代码段时无法设定超时值。
关键段CS与互斥量Mutex
| 创建或初始化 | 销毁 | 进入互斥区域 | 离开互斥区域 |
关键段CS | Initialize- CriticalSection | Delete- CriticalSection | Enter- CriticalSection | Leave- CriticalSection |
互斥量Mutex | CreateMutex | CloseHandle | 请求共享对象的使用权WaitForSingleObject | ReleaseMutex |
关键段不是内核对象,速度快。但是因为enter无时间限制,很容易进入死锁。
互斥量是内核对象,速度慢。同时,互斥量能很好的处理“遗弃”情况,因此在多进程之间可以放心的使用。
事件Event
| 创建 | 销毁 | 使事件有信号 | 使事件无信号 |
| 等待事件有信号 |
事件Event | CreateEvent | CloseHandle | SetEvent | ResetEvent |
| WaitForSingleObject |
手动置位:线程等到事件有信号后,取得信号,但要手动resetevent将事件置为无信号,否则无法同步。自动置位:线程等到事件有信号后,取得信号,系统自动将事件置为无信号。如果要取得同步效果,必须用自动置位。
信号量Semaphore
| 创建 | 销毁 | 递减计数 | 递增计数 |
信号量 Semaphore | Create- Semaphore | CloseHandle | 等待系列函数如WaitForSingleObject | Release- Semaphore |
当前资源数量大于0,表示信号量有信号,等于0表示资源已经耗尽故信号量无信号
互斥量,事件,信号量都是内核对象,可以跨进程使用
互斥对象同步:
#include <windows.h> #include <iostream.h> DWORD WINAPI Fun1Proc(LPVOID lpParameter);//线程函数的声明 DWORD WINAPI Fun2Proc(LPVOID lpParameter); 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,"tickets");//创建互斥对象 if(hmutex) { if(ERROR_ALREADY_EXISTS==GetLastError()) { cout<<"程序已经运行!"<<endl; } } Sleep(4000); } DWORD WINAPI Fun1Proc(LPVOID lpParameter) { while(1) { WaitForSingleObject(hmutex,INFINITE);//得到互斥对象,时间为无限 if(tickets>0) { Sleep(1); cout<<"thread1 sell tickets:"<<tickets--<<endl; } else break; ReleaseMutex(hmutex);//释放互斥对象 } return 0; } DWORD WINAPI Fun2Proc(LPVOID lpParameter) { while(1) { WaitForSingleObject(hmutex,INFINITE); if(tickets>0) cout<<"thread2 sell tickets:"<<tickets--<<endl; else break; ReleaseMutex(hmutex);//释放互斥对象 } return 0; }
临界对象同步.cpp:
#include <windows.h> #include <iostream.h> DWORD WINAPI Fun1Proc(LPVOID lpParameter);//线程函数的声明 DWORD WINAPI Fun2Proc(LPVOID lpParameter); int tickets=100; CRITICAL_SECTION g_cs;//定义一个邻界区对象 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); InitializeCriticalSection(&g_cs);//初始化一个criticalSection Sleep(4000); DeleteCriticalSection(&g_cs);//程序结束前清除对象 } DWORD WINAPI Fun1Proc(LPVOID lpParameter) { while(1) { EnterCriticalSection(&g_cs);//去取得对象所有权 if(tickets>0) { Sleep(1); cout<<"thread1 sell tickets:"<<tickets--<<endl; } else break; LeaveCriticalSection(&g_cs);//释放对象所有权 } return 0; } DWORD WINAPI Fun2Proc(LPVOID lpParameter) { while(1) { EnterCriticalSection(&g_cs);//去取得对象所有权 if(tickets>0) cout<<"thread2 sell tickets:"<<tickets--<<endl; else break; LeaveCriticalSection(&g_cs);//释放对象所有权 } return 0; }
事件对象同步:
#include <windows.h> #include <iostream.h> DWORD WINAPI Fun1Proc(LPVOID lpParameter);//线程函数的声明 DWORD WINAPI Fun2Proc(LPVOID lpParameter); int tickets=100; HANDLE g_hEvent; 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); //g_hEvent=CreateEvent(NULL,FALSE,TRUE,NULL); //第二个参数指明是人工重置的事件对象还是自动重置,Ture为人工重置 //第三个参数表示初始化状态,TRUE为有信号状态 //第四个参数为事件的名字,匿名 g_hEvent=CreateEvent(NULL,FALSE,TRUE,"tickets"); if(g_hEvent) { if(ERROR_ALREADY_EXISTS==GetLastError()) { cout<<"已经有一个实例在运行!"<<endl; return; } } Sleep(4000); } DWORD WINAPI Fun1Proc(LPVOID lpParameter) { while(1) { WaitForSingleObject(g_hEvent,INFINITE);//得到事件对象的信号,时间为无限 if(tickets>0) { Sleep(1); cout<<"thread1 sell tickets:"<<tickets--<<endl; } else break; SetEvent(g_hEvent);//将信号设为有信号状态 } CloseHandle(g_hEvent);//关闭事件对象句柄 return 0; } DWORD WINAPI Fun2Proc(LPVOID lpParameter) { while(1) { WaitForSingleObject(g_hEvent,INFINITE); if(tickets>0) cout<<"thread2 sell tickets:"<<tickets--<<endl; else break; SetEvent(g_hEvent);//将信号设为有信号状态 } return 0; }