码和数据。所以你可以看到在我们的例子里面,主线程和子线程可以共同拥有hout,还可以共同获得home planet和planet1结构体里面的信息。打个比方,进程就像一个房子,线程就像住进房子里面的人,世界的运行并不是由房子发动的,而是由房子中的人来进行的,但是房子提供了人活动的边界和各种房间以及功能性的资源,在同一个房子中所有人原则上都能运用里面所有的房间和东西但是用不了别人房子的内容。就算互相交流也是以人与人为单位互相交流甚至和邻居交流,房子是不能互相交流的。
四、另一种形式的美好。
CRITICAL_SECTION提供了一种非常方便的同步方法并且是在user mode下的而不是kernel mode下实现的,几个相关函数也表达出了特别通俗易懂的解决方式。解决一个问题的方法往往不止一个,我一直都感觉如果把两个相似的东西放在一起对比一下最能体会到二者的不同与奥秘,所以在这一个部分,还是第三部分同样的问题,换一种方式一样能解决这个问题。
我想要在这里介绍的叫做mutex,mutex是Mutual Exclude的缩写,Mutual是彼此的意思,所以这个东西直白的翻译就是彼此的排斥,常见的中文翻译名称叫做互斥体。这个名称就很好理解了,其作用基本和CRITICAL_SECTION基本很像,也是在某一时间内,只有一个单位能够拥有资源(这里用单位没有用线程,因为Mutex是可以跨进程使用的)。如果想使用mutex,首先你的创建一个,ghMutex是一个global的handle对象:
ghMutex = CreateMutex(NULL,FALSE,NULL);
CreateMutex有三个参数,第一个是和createthread第一个参数的含义相同,第二个参数是表示创建者是否是这个mutex的初始拥有者,最后一个参数是你可以给这个mutex对象起一个名字,这样在以后的调用中,你可以使用这个名字唯一的找到这个mutex对象,于是在其他进程中你一样能得到这个mutex对象的句柄。说到获得一个已创建的mutex句柄,你还可以使用OpenMutex函数,这个函数提供获取一个命名mutex的功能并且能指定对这个mutex的权限。对于这部分内容我会在后面稍微扯扯我所知道的。当你有一个mutex对象了之后,想做到同步与控制,你还需要一个重要的函数,WaitForSingleObject,这个函数用来等待一个内核对象的状态变成被激活或者说获取一个激活状态的内核对象,其作用在多线程编程中差不多相当于封装对于C++的地位差不多。那么在介绍怎么用这个函数之前,先来解释一下什么叫激活状态。简单的来说,当你创建一个内核对象的时候,比如Mutex,那么在内核中它就拥有一个初始状态,在我们这个例子中,这是被激活的一种状态,也就是差不多等于我没有被任何人使用,现在十分的空闲。这时候调用WaitForSingleObject,那么一等就等到了,因为这个时候Mutex没事干,此时,内核会将这个mutex变成非激活状态。然后如果有其他线程再等待这个内核对象,那么这个忙碌的,非激活的Mutex就不能被等待,这个线程只能先登在关键资源的外面直至Mutex变成激活状态之后它才能拥有它。那么既然WaitForSingleObject之后Mutex会变成非激活状态,怎么让它重新变成激活状态呢?你只需要调用ReleaseMutex。
说了这么多,现在来具体介绍一下WaitForSingleObject和它的返回值,WaitForSingleObject需要两个参数,一个是对象的handle,一个是最大等待时间。如果在指定时间内等到一个激活的对象,那么就会返回WAIT_OBJECT_0,一般来说这个返回值就意味着你该做和关键资源相关的事情。如果最大等待时间都过了还没有等到一个激活对象,那么将会返回WAIT_TIMEOUT。另外,这个函数还可以返回WAIT_ABANDONED,这个返回值会在一个拥有mutex的线程在没有释放这个mutex的使用权的情况下就终止了,那么这个mutex的状态就变成了abandoned。如果这个函数返回的是一个WAIT_FAILED,那么就是说这个函数在执行的过程中出现了某种错误。所以稍微修改一下MoveOutputToPos函数,利用Mutex替换CRITICAL_SECTION。
void MoveOutputToPos(int x,int y,char* c,bool bString)
{
CONSOLE_SCREEN_BUFFER_INFO csbiInfo;
csbiInfo.dwCursorPosition.X = x;
csbiInfo.dwCursorPosition.Y = y;
DWORD dStatus = WaitForSingleObject(ghMutex,1000*30);
if ( dStatus == WAIT_OBJECT_0 )
{
if (!SetConsoleCursorPosition(hOut,csbiInfo.dwCursorPosition))
{
printf("SetConsoleCursorPosition error!!!!! \r\n");
ReleaseMutex(ghMutex);
return;
}
if(!bString)
printf("%c",c[0]);
else
printf("%s",c);
ReleaseMutex(ghMutex);
}
else
{
ReleaseMutex(ghMutex);
}
}
最后,在main函数中应该用CloseHandle(ghMutex);来释放这个mutex对象。
【更多】这里我想扯的有两个,一个是CRITICAL_SECTION和Mutex之间的区别,第二个是Mutex这类东西是怎么实现对于资源的同步与控制的。
首先CRITICAL_SECTION是在用户模式下的一个实现,而Mutex是工作在内核模式的。这两个的主要差别就是在速度上,如果只工作和实现在用户模式,那么操作系统不需要进行上下文保存,系统调用或者中断等等,需要的指令自然就少,那么结果肯定是快。而Mutex得经历从application的代码用户模式到内核模式的转换,那么需要的工作就多了。其实用户模式和内核模式就是执行的CPU指令,为了不至于让应用程序跑着跑着就搞出了严重的系统错误,CPU一般把指令