操作系统里讲的的进程同步,用的是信号灯,PV操作,P操作看成是申请资源,V操作是看成是交还资源,资源可以有很多解释,比如时间,空间,数据等,而信号量可以看成是资源数目。
在WIN32里多进程用得少,因为进程建立很费劲,分配虚拟内在是其中一个原因,取而代之的是线程,线程可以看成是小进程,是一个进程中活的东西,进程是死的,占有了内存和得到了一些系统资源后就死了,只有启动主线程的时候才活起来,主线程的地位相当重要,主线程一结束进程也就被OS踢出去了。进程间也可以通信,当然要复杂一些,因为地址空间完全不同,用得多的有管道等。
1,互斥对象
一个互斥对象,维护一组数据:当前线程,使用计数,受信状态。PV操作相当于:WaitForSingleObject()和ReleaseMutex(),ReleaseMutex()不一定会将Mutex Release掉,如果计数为1,才会将线程ID改为0,并改受信状态为signed。建立的语句是CreateMutex,释放资源是CloseHandle,进程退出的时候也会自动释放。
2,事件对象
事件对象分两种,一种是人工重置的(ManualReset),影响到WaitForSingleObject()的时候是否由系统设为unsigned,这里有个值得思考的情况:
a,When "Non-ManualReset" use
WaitForSingleObject(event1);
b,When "ManualReset" use
WaitForSingleObject(event1);
ResetEvent(event1);
他们是不是等价的呢?看起来,如果人工重置的,在WaitForSingleObject()后紧接着就ResetEvent(),那不就是系统自动的了吗?
这是不等价的,原因是,在调用完WaitForSingleObject()后,在进入ResetEvent()之前,是有可能被系统的线程调度程序给设成‘就绪’状态,其它的线程执行,这时候又是signed,所以就出问题了。
3,临界区段
临界区程序就是有线程在处理相同的数据,如果线程都在跑各自不同的数据,那异步是完全没有问题的,如果跑相同的程序,就可能有问题,相当于线程撞在了一起,所以叫临界区。用InitializeCriticalSection创建,DeleteCriticalSection释放,进入临界区是EnterCriticalSection,离开是LeaveCriticalSection。感觉这跟互斥对象是一样的,也有个计数器在维护。
下面是两个线程异步售票的程序:
#include "stdafx.h"
#include "windows.h"
CRITICAL_SECTION g_cs;
int tickets=200;
HANDLE g_event_finish;
DWORD WINAPI ThreadProc1(
LPVOID lpParameter // thread data
)
{
// while(tickets>0)
while(TRUE)
{
EnterCriticalSection(&g_cs);
if(tickets>0)
printf("Thread 1 sell tickets %d/n",tickets--);
else
break;
LeaveCriticalSection(&g_cs);
}
printf("tickets sold out/n");
SetEvent(g_event_finish);
return 0;
}
DWORD WINAPI ThreadProc2(
LPVOID lpParameter // thread data
)
{
// while(tickets>0)
while(TRUE)
{
EnterCriticalSection(&g_cs);
if(tickets>0)
printf("Thread 2 sell tickets %d/n",tickets--);
else
break;
LeaveCriticalSection(&g_cs);
}
printf("tickets sold out/n");
SetEvent(g_event_finish);
return 0;
}
int main(int argc, char* argv[])
{
InitializeCriticalSection(&g_cs);
g_event_finish=CreateEvent(NULL,TRUE,FALSE,NULL);
HANDLE h1=CreateThread(NULL,0,ThreadProc1,0,0,NULL);
HANDLE h2=CreateThread(NULL,0,ThreadProc2,0,0,NULL);
CloseHandle(h1);
CloseHandle(h2);
WaitForSingleObject(g_event_finish,INFINITE);
DeleteCriticalSection(&g_cs);
return 0;
}
原来用的while(tickets>0),有问题,反正对临界区变量的操作都放在临界区段,才会没问题。
另外同步和异步差不多,同步只是要控制各自前进的步伐,可以认为是交替异步,需要控制步伐的异步,比如典型的生产者和消费者模型,消费过程前进到‘没有产品剩余的时候’就要控制步伐,同样的道理,生产者生产到仓库里装满产品的时候也要控制一下,这就是同步。相比之下异步要简单得多,只要让公共数据的操作放在临界区段内就行了。