目录
2.函数CreateSemaphore,用于创建信号量,声明如下:
3. 函数WaitForSingleObject,用于申请资源,声明如下:
4.函数ReleaseSemaphore,用于释放资源,声明如下:
5.函数WaitForMultipleObjects,用于等待多个内核对象,声明如下:
基本操作
1.函数CreateThread,用于创建线程,声明如下:
WINBASEAPI
__out
HANDLE
WINAPI
CreateThread(
__in_opt LPSECURITY_ATTRIBUTES lpThreadAttributes,
__in SIZE_T dwStackSize,
__in LPTHREAD_START_ROUTINE lpStartAddress,
__in_opt LPVOID lpParameter,
__in DWORD dwCreationFlags,
__out_opt LPDWORD lpThreadId );
lpThreadAttributes是线程的属性。
dwStackSize是线程的栈大小。
lpStartAddress是线程函数的开始地址。
lpParameter是传送给线程函数的参数。
dwCreationFlags是创建线程标志,比如挂起线程。
lpThreadId是标识这个线程的ID。
2.函数CreateSemaphore,用于创建信号量,声明如下:
CreateSemaphore(
__in_opt LPSECURITY_ATTRIBUTES lpSemaphoreAttributes,
__in LONG lInitialCount,
__in LONG lMaximumCount,
__in_opt LPCSTR lpName );
lpSemaphoreAttributes是信号量的安全属性。
lInitialCount是初始化的信号量。
lMaximumCount是允许信号量增加到最大值。
lpName是信号量的名称。
例如: m_hSemaphore = CreateSemaphore( //在vs中写 CreateSemaphoreA
NULL, // 缺省的安全属性
0, // 初始化为0个信号量
cMax, // 最大为10个信号量
NULL); // 不命名
3. 函数WaitForSingleObject,用于申请资源,声明如下:
WaitForSingleObject(
HANDLE hObject, //指明一个内核对象的句柄
DWORD dwMilliseconds); //等待时间
4.函数ReleaseSemaphore,用于释放资源,声明如下:
ReleaseSemaphore(
__in HANDLE hSemaphore,
__in LONG lReleaseCount,
__out_opt LPLONG lpPreviousCount );
hSemaphore是要增加的信号量句柄。
lReleaseCount是增加的计数。
lpPreviousCount是增加前的数值返回。
5.函数WaitForMultipleObjects,用于等待多个内核对象,声明如下:
DWORD WaitForMultipleObjects(
DWORD nCount,
CONST HANDLE *lpHandles,
BOOL fWaitAll,
DWORD dwMilliseconds );
nCount是句柄的数量,最大值为MAXIMUM_WAIT_OBJECTS(64)。
lpHandles是HANDLE句柄数组的指针,HANDLE 类型可以为(Event,Mutex,Process,Thread,Semaphore)数组。
bWaitAll是等待的类型,如果为TRUE则等待所有信号量有效在往下执行,FALSE 当有其中一个信号量有效时就向下执行。
dwMilliseconds是超时时间,超时后向执行。如果为WSA_INFINITE永不超时。如果没有信号量就会在这死等。
实验一(两人向一个账户存款问题)
问题描述
妈妈和孩子共享一个账户count,初始账户余额为100,妈妈进程程往count中存款100,孩子进程往count中存款200。count是临界资源,妈妈进程和孩子进程要互斥使用count。编写一个实现妈妈和孩子进程互斥的代码。
实验代码
#include <iostream>
#include <cstdlib>
#include <windows.h>
#include <stdio.h>
using namespace std;
DWORD WINAPI mother(LPVOID lpParameter);
DWORD WINAPI child(LPVOID lpParameter);
HANDLE mutex; //API函数返回信号量句柄
int count=100;
int main()
{
HANDLE hThread[2];
DWORD motherID; //妈妈线程的标识符
DWORD childID; //孩子线程的标识符
mutex = CreateSemaphore(NULL, 1, 1, NULL); //缺省安全属性,初始资源为1,最大并发数为1,创建匿名信号量
hThread[0] = CreateThread(NULL, 0, mother, 0, 0, &motherID);
hThread[1] = CreateThread(NULL, 0, child, 0, 0, &childID);
WaitForMultipleObjects(2, hThread, true, INFINITE); //用于等待多个线程,2是线程个数,hThread是句柄数组的指针
printf("count=:%d\n",count);
return 0;
}
DWORD WINAPI mother(LPVOID lpParameter){ //LPVOID是一个void* 数据类型
int n;
WaitForSingleObject(mutex,INFINITE); //INFINITE为无限时间量
n=count;
Sleep(100);
n=n+100;
count=n;
ReleaseSemaphore(mutex, 1, NULL); //信号量资源数加一
return 0;
}
DWORD WINAPI child(LPVOID lpParameter){
int m;
WaitForSingleObject(mutex,INFINITE);
m=count;
m=m+200;
Sleep(100);
count=m;
ReleaseSemaphore(mutex, 1, NULL); //信号量资源数加一
return 0;
}
运行结果
结果分析
加入互斥信号量mutex,使得mother进程和child进程互斥的访问互斥资源count,从而程序既能实现并发,又能保证程序运行结果的可再现性。
实验二 (多个程序合作)
题目描述
如图所示,模拟它们的运行。
实验代码
#include <iostream>
#include <cstdlib>
#include <windows.h>
using namespace std;
DWORD WINAPI FunA(LPVOID lpParameter);
DWORD WINAPI FunB(LPVOID lpParameter);
DWORD WINAPI FunC(LPVOID lpParameter);
HANDLE g_ThreadSema1;
HANDLE g_ThreadSema2;
int main()
{
HANDLE hThread[3];
g_ThreadSema1 = CreateSemaphore(NULL, 0, 1, NULL); //创建匿名信号量,初始资源为零,最大并发数为1,
g_ThreadSema2 = CreateSemaphore(NULL, 0, 1, NULL);
hThread[0] = CreateThread(NULL, 0, FunA, 0, 0, NULL); // 创建线程
hThread[1] = CreateThread(NULL, 0, FunB, 0, 0, NULL); // 创建线程
hThread[2] = CreateThread(NULL, 0, FunC, 0, 0, NULL); // 创建线程
WaitForMultipleObjects(3, hThread, true, INFINITE); //用于等待多个线程,2是线程个数,hThread是句柄数组的指针
return 0;
}
DWORD WINAPI FunA(LPVOID lpParameter){
Sleep(1000);
cout<<"$A"<<endl; //表示创建线程A
Sleep(500);
cout<<"I am A"<<endl; //表示线程A开始运行
ReleaseSemaphore(g_ThreadSema1, 1, NULL); //信号量资源数加一
ReleaseSemaphore(g_ThreadSema2, 1, NULL); //信号量资源数加一
return 0;
}
DWORD WINAPI FunB(LPVOID lpParameter){
cout<<"$B"<<endl; //表示创建线程B
Sleep(100);
WaitForSingleObject(g_ThreadSema1, INFINITE); //等待信号量资源数>0
cout<<"I am B"<<endl; //表示线程B开始运行
Sleep(100);
return 0;
}
DWORD WINAPI FunC(LPVOID lpParameter){
Sleep(100);
cout<<"$C"<<endl; //表示创建线程C
WaitForSingleObject(g_ThreadSema2, INFINITE); //等待信号量资源数>0
Sleep(100);
cout<<"I am C"<<endl; //表示线程C开始运行
return 0;
}
运行结果
结果分析
A,B,C分别相对应于图中的P1,P2,P3;&B,&C,&A表示先后创建了进程B,C,A;由于进程B,C的运行需要等待A运行完释放的信号量才能继续运行。因此A先运行;A运行后,先释放B等待的信号量,然后B开始运行;后释放C等待的信号量,最后C运行。
实验三(售票员和司机合作问题)
问题描述
设公共汽车上,司机和售票员的活动分别是:
司机的活动: 启动车辆;
正常行车;
到站停车;
售票员的活动:关车门;
售票;
开车门;
编写程序实现这两个活动的同步关系。
实验代码
#include <iostream>
#include <windows.h>
#include <cstdlib>
using namespace std;
HANDLE close,stop;
DWORD WINAPI Drive(LPVOID lpParameter);
DWORD WINAPI Conducte(LPVOID lpParameter);
bool g_continue = true; //控制
int main() {
close=CreateSemaphore(NULL,0,1,NULL);//创建关门信号量
stop=CreateSemaphore(NULL,0,1,NULL);//创建停车信号量
HANDLE Driver,Conductor;
Driver = CreateThread(NULL, 0, Drive, 0, 0, NULL); // 创建司机线程
Conductor = CreateThread(NULL, 0, Conducte, 0, 0, NULL); // 创建售票员线程
while(g_continue) {
if(getchar()) {
g_continue=false;
}
}
return 0;
}
//司机进程
DWORD WINAPI Drive(LPVOID lpParameter) {
while(true) {
WaitForSingleObject(close,INFINITE);
cout<<"start the bus"<<endl;
cout<<"run"<<endl;
Sleep(1000);
cout<<"stop the bus"<<endl;
ReleaseSemaphore(stop,1,NULL);
}
return 0;
}
//售票员进程
DWORD WINAPI Conducte(LPVOID lpParameter) {
while(true){
cout<<"close the door"<<endl;
cout<<"sale tickets"<<endl;
ReleaseSemaphore(close,1,NULL);
WaitForSingleObject(stop,INFINITE);
cout<<"open the door"<<endl<<endl;
}
return 0;
}
运行结果
结果分析
首先售票员进程需要先关上车门,售票后,发出关门信号,司机等到关门信号后,开始起动,开车,到站停车后,发出停车信号,售票员等到停车信号后,打开车门...如此循环下去。
实验四(生产者\消费者问题)
问题描述
假定在生产者和消费者之间的公用缓冲池中,具有n个缓冲区,这时可利用互斥信号量mutex实现诸进程对缓冲池的互斥使用;利用信号量empty和full分别表示缓冲池中空缓冲区和满缓冲区的数量。又假定这些生产者和消费者相互等效,只要缓冲池未满,生产者便可将消息送入缓冲池;只要缓冲池未空,消费者便可从缓冲池中取走一个消息。
• Consumer_product.cpp
• 变量定义
const unsigned short SIZE_OF_BUFFER = 10; //缓冲区长度
unsigned short ProductID = 0; //产品号
unsigned short ConsumeID = 0; //将被消耗的产品号
unsigned short in = 0; //产品进缓冲区时的缓冲区下标
unsigned short out = 0; //产品出缓冲区时的缓冲区下标
int g_buffer[SIZE_OF_BUFFER]; //缓冲区是个循环队列
bool g_continue = true; //控制程序结束
HANDLE g_hMutex; //用于线程间的互斥
(HANDLE是对象的句柄,最基本的句柄类型)
HANDLE g_hFullSemaphore; //当缓冲区满时迫使生产者等待
HANDLE g_hEmptySemaphore; //当缓冲区空时迫使消费者等待
DWORD WINAPI Producer(LPVOID); //生产者线程(DWORD是无符号整型)
DWORD WINAPI Consumer(LPVOID); //消费者线程
• 部分函数
CreateMutex();创建互斥信号量
CreateSemaphore ();创建同步信号量
CreateThread ();创建线程
WaitForSingleObject (); 等待信号量
ReleaseMutex (); 释放信号量
ReleaseSemaphore();
Sleep();执行挂起一段时间
实验代码
#include <windows.h>
#include <iostream>
#include <stdio.h>
const unsigned short SIZE_OF_BUFFER = 10; //缓冲区长度
unsigned short ProductID = 0; //产品号
unsigned short ConsumeID = 0; //将被消耗的产品号
unsigned short in = 0; //产品进缓冲区时的缓冲区下标
unsigned short out = 0; //产品出缓冲区时的缓冲区下标
int g_buffer[SIZE_OF_BUFFER]; //缓冲区是个循环队列
bool g_continue = true; //控制程序结束
HANDLE g_hMutex; //用于线程间的互斥
HANDLE g_hFullSemaphore; //当缓冲区满时迫使生产者等待
HANDLE g_hEmptySemaphore; //当缓冲区空时迫使消费者等待
DWORD WINAPI Producer(LPVOID); //生产者线程
DWORD WINAPI Consumer(LPVOID); //消费者线程
int main()
{ //创建各个互斥信号
g_hMutex = CreateMutex(NULL,FALSE,NULL);
g_hFullSemaphore = CreateSemaphore(NULL,SIZE_OF_BUFFER,SIZE_OF_BUFFER,NULL);
g_hEmptySemaphore = CreateSemaphore(NULL,0,SIZE_OF_BUFFER,NULL);
//调整下面的数值,可以发现,当生产者个数多于消费者个数时,
//生产速度快,生产者经常等待消费者;反之,消费者经常等待
const unsigned short PRODUCERS_COUNT = 3; //生产者的个数
const unsigned short CONSUMERS_COUNT = 1; //消费者的个数
//总的线程数
const unsigned short THREADS_COUNT = PRODUCERS_COUNT+CONSUMERS_COUNT;
HANDLE hThreads[PRODUCERS_COUNT]; //各线程的handle
DWORD producerID[CONSUMERS_COUNT]; //生产者线程的标识符
DWORD consumerID[THREADS_COUNT]; //消费者线程的标识符
//创建生产者线程
for (int i=0;i<PRODUCERS_COUNT;++i){
hThreads[i]=CreateThread(NULL,0,Producer,NULL,0,&producerID[i]);
if (hThreads[i]==NULL) return -1;
}
//创建消费者线程
for ( int i=0;i<CONSUMERS_COUNT;++i){
hThreads[PRODUCERS_COUNT+i]=CreateThread(NULL,0,Consumer,NULL,0,&consumerID[i]);
if (hThreads[i]==NULL) return -1;
}
while(g_continue){
if(getchar()){ //按回车后终止程序运行
g_continue = false;
}
}
return 0;
}
//生产一个产品。简单模拟了一下,仅输出新产品的ID号
void Produce(){
std::cerr << "Producing " << ++ProductID << " ... ";
std::cerr << "Succeed" << std::endl;
}
//把新生产的产品放入缓冲区
void Append(){
std::cerr << "Appending a product ... ";
g_buffer[in] = ProductID;
in = (in+1)%SIZE_OF_BUFFER;
std::cerr << "Succeed" << std::endl;
//输出缓冲区当前的状态
for (int i=0;i<SIZE_OF_BUFFER;++i){
std::cout << i <<": " << g_buffer[i];
if (i==in) std::cout << " <-- produce";//生产
if (i==out) std::cout << " <-- consume";//消费
std::cout << std::endl;
}
}
//从缓冲区中取出一个产品
void Take(){
std::cerr << "Taking a product ... ";
ConsumeID = g_buffer[out];
out = (out+1)%SIZE_OF_BUFFER;
std::cerr << "Succeed" << std::endl;
//输出缓冲区当前的状态
for (int i=0;i<SIZE_OF_BUFFER;++i){
std::cout << i <<": " << g_buffer[i];
if (i==in) std::cout << " <-- produce";//生产
if (i==out) std::cout << " <-- consume";//消费
std::cout << std::endl;
}
}
//消耗一个产品
void Consume(){
std::cerr << "Consuming " << ConsumeID << " ... ";
std::cerr << "Succeed" << std::endl;
}
//生产者
DWORD WINAPI Producer(LPVOID lpPara){
while(g_continue){
WaitForSingleObject(g_hFullSemaphore,INFINITE);
WaitForSingleObject(g_hMutex,INFINITE);
Produce();
Append();
Sleep(1500);
ReleaseMutex(g_hMutex);
ReleaseSemaphore(g_hEmptySemaphore,1,NULL);
}
return 0;
}
//消费者
DWORD WINAPI Consumer(LPVOID lpPara){
while(g_continue){
WaitForSingleObject(g_hEmptySemaphore,INFINITE);
WaitForSingleObject(g_hMutex,INFINITE);
Take();
Consume();
Sleep(1500);
ReleaseMutex(g_hMutex);
ReleaseSemaphore(g_hFullSemaphore,1,NULL);
}
return 0;
}
运行结果
结果分析
由于先创建了三个生产者和一个消费者,三个消费者并发执行消费者进程,生产3个产品并放在缓冲区后,释放一个empty信号量,消费者等到一个empty信号量后开始取产品使用,然后释放一个full信号量,生产者继续生产,然后消费者再取,依次交替进行下去。