多线程编程学习笔记

多线程编程学习笔记
一、win32进程、线程基础
1、进程
从win32 的角度来看,进程含有内存和资源。被进程拥有的内存,理论上可以高达2gb。资源包括核心对象,如file handles、线程,user资源,如对话框、字符串,GDI资源,如Device Context 和 Brushes。
程序是指令和数据的集合,而程序要想执行,必须为其分配内存和资源,程序加上为其分配的内存和资源就成为了进程。
在x86计算机体系架构下,内存根据cpu的特征分为以下三个部分,代码部分(cpu指令指针指向的部分),数据部分(cpu数据段寄存器记录的部分)和堆栈部分(cpu堆栈寄存器记录的部分)。
2、线程
线程为进程的动态执行过程,cpu的指令指针开始从该程序中不断的取指令,并开始执行取到的每一条指令,这种不断进行的取指令和执行指令的过程就是线程。
3、多进程与多线程的取舍
人们一旦接触到线程就会想到为什么不用已经熟悉的进程而要用完全陌生的线程,那么为什么我们要使用线程呢?使用多线程而代替多进程的主要原因是多线程运行比多进程运行耗费的系统资源要少。
比如在www服务器领域,如果使用多进程,每一个request都要创建一个进程,而使用多线程,只需为为每个request创建一个线程。如果有100个请求,使用多进程,系统就会开辟100份资源来响应这些请求,如果使用多线程,系统只需要开辟一份资源来响应这些请求。
4、Context switch
在一个抢先式多任务系统中,操作系统会小心的确保每一个线程都有机会执行。它依赖硬件的协助以及许多的簿记工作。当硬件计时器认为某个线程执行的已经够久了,就会发出一个中断,于是cpu取得目前运行线程的状态,也就是把当前所有寄存器的内容拷贝到堆栈中,再把它从堆栈拷贝到一个CONTEXT结构中,这样便储存了线程的的状态,下次调度该线程时,只需取得context结构中的值,恢复线程执行状态,开始继续执行。这样的过程成为context switch。
5、Race Conditions
在一个合作型多任务系统中,操作系统必须得到程序的容许才能够改变线程。但是在抢先式多任务系统中,控制权被强制转移,也因此两个线程之间的执行次序变得不可预期,这就造成了Race conditions。
线程中的指令会被不断的中断而转而执行别的线程中的指令。
例1.1 raceconditions.cpp

#include "windows.h"
#include <fstream>

using namespace std;

ofstream out("output.txt");

DWORD WINAPI ThreadFuncOne(LPVOID _param);
DWORD WINAPI ThreadFuncTwo(LPVOID _param);
DWORD WINAPI ThreadFuncThree(LPVOID _param);
DWORD WINAPI ThreadFuncForth(LPVOID _param);

int main(void){
HANDLE hThreadOne = NULL;
HANDLE hThreadTwo = NULL;
HANDLE hThreadThree = NULL;
HANDLE hThreadForth = NULL;
hThreadOne = ::CreateThread(NULL, 0, ThreadFuncOne, (void *)"我是one", 0, NULL);
hThreadTwo = ::CreateThread(NULL, 0, ThreadFuncTwo, (void *)"我是two", 0, NULL);
hThreadThree = ::CreateThread(NULL, 0, ThreadFuncThree, (void *)"我是three", 0, NULL);
hThreadForth = ::CreateThread(NULL, 0, ThreadFuncForth, (void *)"我是forth", 0, NULL);
for(int i = 0;i < 10; i++){
out<<"main线程开始执行"<<endl;
out<<"我是main线程"<<endl;
out<<"main线程结束执行"<<endl;
}
::WaitForSingleObject(hThreadOne, INFINITE);
::WaitForSingleObject(hThreadTwo, INFINITE);
::WaitForSingleObject(hThreadThree, INFINITE);
::WaitForSingleObject(hThreadForth, INFINITE);
if(hThreadOne)
::CloseHandle(hThreadOne);
if(hThreadTwo)
::CloseHandle(hThreadTwo);
if(hThreadThree)
::CloseHandle(hThreadThree);
if(hThreadForth)
::CloseHandle(hThreadForth);
out.close();
return 0;
}

DWORD WINAPI ThreadFuncOne(LPVOID _param){
for(int i = 0;i< 10; i++){
out<<"写入开始"<<endl;
out<<(LPCSTR)_param<<endl;
out<<"写入结束"<<endl;
}
return 0;
}

DWORD WINAPI ThreadFuncTwo(LPVOID _param){
for(int i = 0;i< 10; i++){
out<<"写入开始"<<endl;
out<<(LPCSTR)_param<<endl;
out<<"写入结束"<<endl;
}
return 0;
}

DWORD WINAPI ThreadFuncThree(LPVOID _param){
for(int i = 0;i< 10; i++){
out<<"写入开始"<<endl;
out<<(LPCSTR)_param<<endl;
out<<"写入结束"<<endl;
}
return 0;
}


DWORD WINAPI ThreadFuncForth(LPVOID _param){
for(int i = 0;i< 10; i++){
out<<"写入开始"<<endl;
out<<(LPCSTR)_param<<endl;
out<<"写入结束"<<endl;
}
return 0;
}


二、线程的创建与销毁
创建线程有三种方法:一是直接调用win32 api创建线程的函数CreateThread;二是调用c类库函数_beginthreadex;三是使用mfc库函数AfxBeginThread,本部分只学习利用win32 api处理线程。
Win32 API是Windows操作系统内核与应用程序之间的界面,它将内核提供的功能进行函数包装,应用程序通过调用相关函数而获得相应的系统功能。
(1)创建线程

HANDLE CreateThread(LPSECURITY_ATTRIBUTES lpThreadAttributes,DWORD dwStackSize,LPTHREAD_START_ROUTINE lpStartAddress,LPVOID lpParameter,DWORD dwCreationFlags,LPDWORD lpThreadId);

如果创建成功则返回线程的句柄,否则返回NULL。创建了新的线程后,该线程就开始启动执行了。但如果在dwCreationFlags中使用了CREATE_SUSPENDED,那么线程并不马上执行,而是先挂起,等到调用ResumeThread后才开始启动线程,在这个过程中可以调用下面这个函数来设置线程的优先权:
BOOL SetThreadPriority(HANDLE hThread,int nPriority);

当调用线程的函数返回后,线程自动终止。如果需要在线程的执行过程中终止则可调用函数:

VOID ExitThread(DWORD dwExitCode);

如果在线程的外面终止线程,则可调用下面的函数:

BOOL TerminateThread(HANDLE hThread,DWORD dwExitCode);

但应注意: 该函数可能会引起系统不稳定,而且线程所占用的资源也不释放。因此,一般情况下,建议不要使用该函数。

如果要终止的线程是进程内的最后一个线程,则线程被终止后相应的进程也应终止。
(2)终止线程
  若要终止线程的运行,可以使用下面的方法:
  • 线程函数返回(最好使用这种方法)。
  • 通过调用 ExitThread 函数,线程将自行撤消(最好不要使用这种方法)。
  • 同一个进程或另一个进程中的线程调用 TerminateThread 函数(应该避免使用这种方法)。
  • 包含线程的进程终止运行(应该避免使用这种方法)。
(a)线程函数返回
  始终都应该将线程设计成这样的形式,即当想要线程终止运行时,它们就能够返回。这是确保所有线程资源被正确地清除的唯一办法。
  如果线程能够返回,就可以确保下列事项的实现:
  • 在线程函数中创建的所有 C++ 对象均将通过它们的撤消函数正确地撤消。
  • 操作系统将正确地释放线程堆栈使用的内存。
  • 系统将线程的退出代码(在线程的内核对象中维护)设置为线程函数的返回值。
  • 系统将递减线程内核对象的使用计数。
(b)使用 ExitThread 函数
  可以让线程调用 ExitThread 函数,以便强制线程终止运行:

  VOID ExitThread(DWORD dwExitCode);

该函数将终止线程的运行,并导致操作系统清除该线程使用的所有操作系统资源。但是,C++ 资源(如 C++ 类对象)将不被撤消。由于这个原因,最好从线程函数返回,而不是通过调用 ExitThread 来返回。
  当然,可以使用 ExitThread 的 dwExitThread 参数告诉系统将线程的退出代码设置为什么。ExitThread 函数并不返回任何值,因为线程已经终止运行,不能执行更多的代码。

(c)使用 TerminateThread 函数
  调用 TerminateThread 函数也能够终止线程的运行:
  BOOL TerminateThread(HANDLE hThread, DWORD dwExitCode);

  与 ExitThread 不同,ExitThread 总是撤消调用的线程,而 TerminateThread 能够撤消任何线程。hThread 参数用于标识被终止运行的线程的句柄。当线程终止运行时,它的退出代码成为你作为 dwExitCode 参数传递的值。同时,线程的内核对象的使用计数也被递减。

  注意 TerminateThread 函数是异步运行的函数,也就是说,它告诉系统你想要线程终止运行,但是,当函数返回时,不能保证线程被撤消。如果需要确切地知道该线程已经终止运行,必须调用 WaitForSingleObject 或者类似的函数,传递线程的句柄。
  设计良好的应用程序从来不使用这个函数,因为被终止运行的线程收不到它被撤消的通知。线程不能正确地清除,并且不能防止自己被撤消。

  注意 当使用返回或调用 ExitThread 的方法撤消线程时,该线程的内存堆栈也被撤消。但是,如果使用 TerminateThread,那么在拥有线程的进程终止运行之前,系统不撤消该线程的堆栈。Microsoft故意用这种方法来实现 TerminateThread。如果其他仍然正在执行的线程要引用强制撤消的线程堆栈上的值,那么其他的线程就会出现访问违规的问题。如果将已经撤消的线程的堆栈留在内存中,那么其他线程就可以继续很好地运行。
  此外,当线程终止运行时,DLL 通常接收通知。如果使用 TerminateThread 强迫线程终止,DLL 就不接收通知,这能阻止适当的清除。

(d)在进程终止运行时撤消线程
  我们知道,进程函数 ExitProcess 和 TerminateProcess 也可以用来终止线程的运行。差别在于这些线程将会使终止运行的进程中的所有线程全部终止运行。另外,由于整个进程已经被关闭,进程使用的所有资源肯定已被清除。这当然包括所有线程的堆栈。这两个函数会导致进程中的剩余线程被强制撤消,就像从每个剩余的线程调用 TerminateThread 一样。显然,这意味着正确的应用程序清除没有发生,即 C++ 对象撤消函数没有被调用,数据没有转至磁盘等等。
线程终止运行时发生的操作
  当线程终止运行时,会发生下列操作:
  • 线程拥有的所有用户对象均被释放。在 Windows 中,大多数对象是由包含创建这些对象的线程的进程拥有的。但是一个线程拥有两个用户对象,即窗口和挂钩。当线程终止运行时,系统会自动撤消任何窗口,并且卸载线程创建的或安装的任何挂钩。其他对象只有在拥有线程的进程终止运行时才被撤消。
  • 线程的退出代码从 STILL_ACTIVE 改为传递给 ExitThread 或 TerminateThread 的代码。
  • 线程内核对象的状态变为已通知。
  • 如果线程是进程中最后一个活动线程,系统也将进程视为已经终止运行。
  • 线程内核对象的使用计数递减 1。
  当一个线程终止运行时,在与它相关联的线程内核对象的所有未结束的引用关闭之前,该内核对象不会自动被释放。

  一旦线程不再运行,系统中就没有别的线程能够处理该线程的句柄。然而别的线程可以调用 GetExitcodeThread 来检查由 hThread 标识的线程是否已经终止运行。如果它已经终止运行,则确定它的退出代码:

  BOOL GetExitCodeThread(HANDLE hThread, PDOWRD pdwExitCode);

  退出代码的值在 pdwExitCode 指向的 DWORD 中返回。如果调用 GetExitCodeThread 时线程尚未终止运行,该函数就用 STILL_ACTIVE 标识符(定义为 0x103)填入 DWORD。如果该函数运行成功,便返回 TRUE。
三、线程同步
在线程体内,如果该线程完全独立,与其他线程没有数据存取等资源操作上的冲突,则可按照通常单线程的方法进行编程。但是,在多线程处理时情况常常不是这样,线程之间经常要同时访问一些资源。由于对共享资源进行访问引起冲突是不可避免的,为了解决这种线程同步问题,Win32 API提供了四种同步控制对象来帮助程序员解决共享资源访问冲突,这四种方式为:Cretical Section,Mutual Exculution,Semphore和Event。在介绍这些同步对象之前先介绍一下等待函数,因为所有控制对象的访问控制都要用到这个函数。
Win32 API提供了一组能使线程阻塞其自身执行的等待函数。这些函数在其参数中的一个或多个同步对象产生了信号,或者超过规定的等待时间才会返回。在等待函数未返回时,线程处于等待状态,此时线程只消耗很少的CPU时间。使用等待函数既可以保证线程的同步,又可以提高程序的运行效率。最常用的等待函数是:

DWORD WaitForSingleObject(HANDLE hHandle,DWORD dwMilliseconds);

而函数WaitForMultipleObject可以用来同时监测多个同步对象,该函数的声明为:

DWORD WaitForMultipleObject(DWORD nCount,CONST HANDLE *lpHandles,BOOL bWaitAll,DWORD dwMilliseconds);

1、Critical Section ,也叫临界区
(1)临界区对象的创建
CRITICAL_SECTION cs;
(2)临界区对象的初始化
void InitializeCriticalSection( LPCRITICAL_SECTION lpCriticalSection );
(3)进入临界区
void EnterCriticalSection(LPCRITICAL_SECTION lpCriticalSection );
(4)从临界区出来
void LeaveCriticalSection(LPCRITICAL_SECTION lpCriticalSection );
(5)释放临界区对象
void DeleteCriticalSection(LPCRITICAL_SECTION lpCriticalSection );

(6)使用临界区同步线程实例:例3.1

//模拟生产者消费者问题
#include <windows.h>
#include <process.h>
#include <fstream>
#include <iostream>


#define MAX_NUMBER 100
#define MIN_NUMBER 0

using namespace std;

//产品数量
int productNumber = 0;

ofstream out("a.txt");

CRITICAL_SECTION cs;

DWORD WINAPI ThrdProducer(LPVOID param){
while(true){
if(productNumber < MAX_NUMBER){
EnterCriticalSection(&cs);
productNumber++;
out<<"生产了一个产品"<<endl;
out<<"当前产品数量为"<<productNumber<<endl;
::LeaveCriticalSection(&cs);
}else{
Sleep(10);
}
}
return 0;
}
DWORD WINAPI ThrdConsumer(LPVOID param){
while(true){
if(productNumber > MIN_NUMBER){
::EnterCriticalSection(&cs);
productNumber--;
out<<"消费了一个产品"<<endl;
out<<"当前产品数量为"<<productNumber<<endl;
::LeaveCriticalSection(&cs);
}else{
Sleep(10);
}
}
return 0;
}

int main(){
InitializeCriticalSection(&cs);
HANDLE hThread1 = NULL;
HANDLE hThread2 = NULL;
hThread1 = CreateThread(NULL, 0, ThrdProducer, NULL, 0, NULL);
hThread2 = CreateThread(NULL, 0, ThrdConsumer, NULL, 0, NULL);
Sleep(1000);
out.close();
::TerminateThread(hThread1, NULL);
::TerminateThread(hThread2, NULL);
if(hThread1){
::CloseHandle(hThread1);
}
if(hThread2){
::CloseHandle(hThread2);
}
::DeleteCriticalSection(&cs);
return 0;
}

2、Mutex,也叫互斥对象
Win32的mutex与 critical section非常类似,它可以实现跨进程的线程同步,但是速度比critical section慢,锁住一个未被拥有的mutex比锁住一个未被拥有的critical section需要多花费100倍时间。
(1)创建mutex
HANDLE CreateMutex(
LPSECURITY_ATTRIBUTES lpMutexAttributes,
BOOL bInitialOwner,
LPCTSTR lpName
);
(2)打开mutex
HANDLE WINAPI OpenMutex(
__in DWORD dwDesiredAccess,
__in BOOL bInheritHandle,
__in LPCTSTR lpName
);
(3)释放mutex
此处在主线程也就是main方法中一定要记住创建完该对象,并运行完主线程代码后,调用该方法,否则该线程一直不释放该对象,该对象一直处于非激发状态,其他线程一直等不到该对象,致使程序死锁。
BOOL ReleaseMutex(
HANDLE hMutex
);

(4)实例3.2

//模拟生产者消费者问题
#include <windows.h>
#include <process.h>
#include <fstream>
#include <iostream>


#define MAX_NUMBER 100
#define MIN_NUMBER 0

using namespace std;

//产品数量
int productNumber = 0;

ofstream out("output.txt");

HANDLE hMutexObj = NULL;

DWORD WINAPI ThrdProducer(LPVOID param){
//::OpenMutex(SYNCHRONIZE, TRUE, (LPCWSTR)"producerMutex");

while(true){
if(productNumber < MAX_NUMBER){
::WaitForSingleObject(hMutexObj, INFINITE);
productNumber++;
out<<"生产了一个产品"<<endl;
out<<"当前产品数量为"<<productNumber<<endl;
::ReleaseMutex(hMutexObj);
}else{
Sleep(10);
}
}
return 0;
}
DWORD WINAPI ThrdConsumer(LPVOID param){
//::OpenMutex(SYNCHRONIZE, TRUE, (LPCWSTR)"producerMutex");
while(true){
if(productNumber > MIN_NUMBER){
::WaitForSingleObject(hMutexObj, INFINITE);
productNumber--;
out<<"消费了一个产品"<<endl;
out<<"当前产品数量为"<<productNumber<<endl;
::ReleaseMutex(hMutexObj);
}else{
Sleep(10);
}
}
return 0;
}

int main(){
hMutexObj = ::CreateMutex(NULL, TRUE, (LPCWSTR)"producerMutex");
HANDLE hThread1 = NULL;
HANDLE hThread2 = NULL;
hThread1 = CreateThread(NULL, 0, ThrdProducer, NULL, 0, NULL);
hThread2 = CreateThread(NULL, 0, ThrdConsumer, NULL, 0, NULL);
::ReleaseMutex(hMutexObj);//此处忘记了释放互斥对象,浪费了很长时间才把错误定位到此处
Sleep(100);
out.close();
::TerminateThread(hThread1, NULL);
::TerminateThread(hThread2, NULL);
if(hThread1){
::CloseHandle(hThread1);
}
if(hThread2){
::CloseHandle(hThread2);
}
return 0;
}

3、Semphore,也叫信号量
Win32中的一个semaphore可以被锁住最多n次,其中n是semphore被产生时指定的。n常常被设计用来代表“可以锁住一份资源的线程个数”。当n为1时,semphore的作用和mutex是一样的。
应该在什么场景下用semphore呢,考虑这样一个场景,一个名叫steve的人想租一辆车,租车店柜台后面坐了好几位租车代理人。Steve告诉租车代理人说他想要租一部车,接待他的那位租车代理人看到现在还剩下3辆车,于是开始写派车单,但是在他写派车单的时候,又有三个人过来租车,结果等他写完派车单时,车已经被另外的三个人租走了。
如果写一个程序来解决出租车的问题,方法之一就是加上一个mutex保护之。但是每辆车都要一个mutex,这样花费的mutex太多了。另一种方法是只用一个mutex,但是用一个mutex后,一次只能租出去一辆车,显然效率太低。
如果用semphore,创建一个semphore,其n设为和车辆数一样多,当租出去一辆车时,n减1。
(1)产生semphore
HANDLE WINAPI CreateSemaphoreEx(
__in LPSECURITY_ATTRIBUTES lpSemaphoreAttributes,
__in LONG lInitialCount,
__in LONG lMaximumCount,
__in LPCTSTR lpName,
DWORD dwFlags,
__in DWORD dwDesiredAccess
);
(2)得到一个semphore(n-1)
DWORD WaitForSingleObject(
HANDLE hHandle,
DWORD dwMilliseconds
);

(3)释放一个semphore(n+1)
BOOL ReleaseSemaphore(
HANDLE hSemaphore,
LONG lReleaseCount,
LPLONG lpPreviousCount
);
(4)例3.3
//模拟生产者消费者问题
//Semphore较之mutex效率非常低
#include <windows.h>
#include <process.h>
#include <fstream>
#include <iostream>


#define MAX_NUMBER 100
#define MIN_NUMBER 0

using namespace std;

//产品数量
int productNumber = 0;

ofstream out("output.txt");

HANDLE hSemphoreObj = NULL;

DWORD WINAPI ThrdProducer(LPVOID param){
while(true){
if(productNumber < MAX_NUMBER){
::WaitForSingleObject(hSemphoreObj, INFINITE);
productNumber++;
out<<"生产了一个产品"<<endl;
out<<"当前产品数量为"<<productNumber<<endl;
::ReleaseSemaphore(hSemphoreObj, 1, NULL);
}else{
Sleep(10);
}
}
return 0;
}
DWORD WINAPI ThrdConsumer(LPVOID param){
//::OpenMutex(SYNCHRONIZE, TRUE, (LPCWSTR)"producerMutex");
while(true){
if(productNumber > MIN_NUMBER){
::WaitForSingleObject(hSemphoreObj, INFINITE);
productNumber--;
out<<"消费了一个产品"<<endl;
out<<"当前产品数量为"<<productNumber<<endl;
::ReleaseSemaphore(NULL, 1, NULL);
}else{
Sleep(10);
}
}
return 0;
}

int main(){
hSemphoreObj = ::CreateSemaphore(NULL, 1, 1, (LPCWSTR)"HelloSemphore");
HANDLE hThread1 = NULL;
HANDLE hThread2 = NULL;
hThread1 = CreateThread(NULL, 0, ThrdProducer, NULL, 0, NULL);
hThread2 = CreateThread(NULL, 0, ThrdConsumer, NULL, 0, NULL);
::ReleaseSemaphore(hSemphoreObj, 1, NULL);
Sleep(100);
out.close();
::TerminateThread(hThread1, NULL);
::TerminateThread(hThread2, NULL);
if(hThread1){
::CloseHandle(hThread1);
}
if(hThread2){
::CloseHandle(hThread2);
}
return 0;
}
4、Event
Event有两种状态,激发状态和非激发状态,整个同步过程也就是两种状态的转换。
(1)创建event

HANDLE CreateEvent(
LPSECURITY_ATTRIBUTES lpEventAttributes, // SD
BOOL bManualReset, // reset type
BOOL bInitialState, // initial state
LPCTSTR lpName // object name
);

该函数创建一个Event同步对象,并返回该对象的Handle,该函数的参数含义分别为:
(a)、lpEventAttributes为事件对象的安全属性,一般设为NULL。
(b)、bManualReset为创建的事件对象是自动由激发状态变为非激发状态,还是手动调用ResetEvent将事件对象从激发状态变为非激发状态。如果其值为false,事件对象置为激发状态时,它会唤醒一个线程并立马变为非激发状态。如果其值为true,事件对象置为激发状态时,它会唤醒一个线程后接着唤醒其它线程而一直处于激发状态,直到调用ResetEvent将其由激发状态置为非激发状态。
创建的Event是自动复位还是人工复位,
(c)、bInitialState事件初始状态。值为true时处于激发状态,为false时处于未激发状态。
(d)、lpName 事件对象名。
一个Event被创建以后,可以用OpenEvent()API来获得它的Handle,用CloseHandle()来关闭它,用WaitForSingleObject()或WaitForMultipleObjects()来等待。
PulseEvent()是一个比较有意思的使用方法,正如这个API的名字,它使一个Event对象的状态发生一次脉冲变化,从无信号变成有信号再变成无信号,而整个操作是原子的,对自动复位的Event对象,它仅唤醒第一个等到该事件的thread(如果有),而对于人工复位的Event对象,它唤醒所有等待的thread。
(2)event状态变为激发
BOOL SetEvent(
HANDLE hEvent
);
(3)event状态变为非激发
BOOL ResetEvent(
HANDLE hEvent
);

(4)实例3.4
当程序启动时,创建event(事件),CreateEvent的第二个参数bManualReset为FALSE时,创建的该事件通过SetEvent变为激发状态时,会唤醒等待该事件的线程,也就是该线程从waitforsingleobject函数后面开始执行,唤醒该线程后,该事件立马从激发状态自动变为非激发状态。
当程序启动时,创建事件对象,CreateEvent的第二个参数bManualReset为TRUE时,创建的该事件对象通过SetEvent变为激发状态时,会唤醒等待该事件的线程,也就是该线程会从WaitForSingleObject函数后面开始执行,此时,该事件一直处于激发状态,直到调用ResetEvent将该事件对象变为非激发状态。

//模拟生产者消费者问题

#include <windows.h>
#include <process.h>
#include <fstream>
#include <iostream>


#define MAX_NUMBER 100
#define MIN_NUMBER 0

using namespace std;

//产品数量
int productNumber = 0;

ofstream out("output.txt");

HANDLE hEventObj = NULL;

DWORD WINAPI ThrdProducer(LPVOID param){
while(true){
if(productNumber < MAX_NUMBER){
::WaitForSingleObject(hEventObj, INFINITE);
productNumber++;
out<<"生产了一个产品"<<endl;
out<<"当前产品数量为"<<productNumber<<endl;
SetEvent(hEventObj);
}else{
Sleep(10);
}
}
return 0;
}
DWORD WINAPI ThrdConsumer(LPVOID param){
//::OpenMutex(SYNCHRONIZE, TRUE, (LPCWSTR)"producerMutex");
while(true){
if(productNumber > MIN_NUMBER){
::WaitForSingleObject(hEventObj, INFINITE);
productNumber--;
out<<"消费了一个产品"<<endl;
out<<"当前产品数量为"<<productNumber<<endl;
SetEvent(hEventObj);
}else{
Sleep(10);
}
}
return 0;
}

int main(){
hEventObj = ::CreateEvent(NULL, FALSE, FALSE, (LPCWSTR)"HelloEvent");
::SetEvent(hEventObj);
::WaitForSingleObject(hEventObj, INFINITE);
HANDLE hThread1 = NULL;
HANDLE hThread2 = NULL;
hThread1 = CreateThread(NULL, 0, ThrdProducer, NULL, 0, NULL);
hThread2 = CreateThread(NULL, 0, ThrdConsumer, NULL, 0, NULL);
SetEvent(hEventObj);
Sleep(100);
out.close();
::TerminateThread(hThread1, NULL);
::TerminateThread(hThread2, NULL);
if(hThread1){
::CloseHandle(hThread1);
}
if(hThread2){
::CloseHandle(hThread2);
}
return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值