学习windows api 线程

通过Windows API实现多线程下的哲学家就餐问题,演示如何利用临界区和互斥量避免死锁。

对于线程的概念,可能也就只是停留在概念的基础上吧,偶尔想起哲学家吃饭问题,闲来无心,找到了一段代码(自http://edu.codepub.com/2009/0527/4726.php),做了测试,然后自己一句一句的把里面的每一句搞清楚了,然后对上面的代码做了些小小的修改吧,自己觉得这么弄完了之后更觉得对,欢迎拍砖。

#include <windows.h>
#include <iostream.h>
#include <process.h>
#include <stdlib.h>
#include <time.h>


#define NUM_OF_PH 5 //哲学家的个数

unsigned int __stdcall philosopher(LPVOID);

void thinking(int);
void eating(int);
void wait_to_eat(int);

inline void outline(int ,const char *);

//全局变量
CRITICAL_SECTION crout;//这个变量用来保证输出时不会竞争

//CRITICAL_SECTION fork[NUM_OF_PH];//定义五个临界变量,代表五更筷子
//修改之后,用了互斥变量两
void *mutex[NUM_OF_PH + 1];

int main(int argc,char *argv[])
{
 void * hthread[NUM_OF_PH];
 int i;
 unsigned int threadid[NUM_OF_PH];
 int arg[NUM_OF_PH];
 int count = NUM_OF_PH;
 unsigned long retval;


  InitializeCriticalSection(&crout);
 //初始化临界变量
 for(i=0;i<NUM_OF_PH;i++)
 {
 //   InitializeCriticalSection(fork + i);
    mutex[i] = CreateMutex(NULL,false,NULL);
 }
 mutex[NUM_OF_PH] = mutex[0];
 //创建五个哲学家
 for(i = 0; i<NUM_OF_PH;i++)
 {
    arg[i] = i;
    hthread[i] = (void *)_beginthreadex(NULL,0,philosopher,(LPVOID)(arg + i),0,threadid+i);
    if((int)hthread[i] == -1)//如果线程创建失败返回-1
    {
  cerr << "error while create thread " << i <<endl;
  cerr << "error code : "<< GetLastError() <<endl;
    }
 }
 //等待所有线程结束
 retval = WaitForMultipleObjects(NUM_OF_PH,hthread,true,INFINITE);//等待多个线程
 if(retval == WAIT_FAILED)
 {
    cerr<< "wait error,error code: "<<GetLastError()<<endl;
 }
 for(i = 0; i<NUM_OF_PH;i++)
 {
    if(CloseHandle(hthread[i]) == false)//关闭句柄
    {
  cerr << "error while close thread " <<i<<endl;
  cerr << "error code: "<<GetLastError()<<endl;
    }
 }
 return 0;
}

/*******************
哲学家的行为
吃饭,等待,思考
*******************/

unsigned int __stdcall philosopher(LPVOID k)
{
 int n = *(int *) k;
 outline(n," is in!");

 srand(time(NULL));
 while(true)
 {
    thinking(n);
    wait_to_eat(n);
    eating(n);
 }

 outline(n," is out!");

 return n;
}
/*************
思考
随机一段时间
*************/
void thinking(int k)
{
 outline(k," is thinking...");

 Sleep((rand()%1000) *NUM_OF_PH);
 //Sleep((1%1000) *NUM_OF_PH);
}
/*************
吃饭
随机一段时间
*************/
void eating(int k)
{
 outline(k," is eating...");
 Sleep((rand()%1000) *NUM_OF_PH);

 /*
 LeaveCriticalSection(fork + (k+1)%NUM_OF_PH);//放下右边的筷子
 outline(k," give left");
 LeaveCriticalSection(fork + k);//放下左边的筷子
 outline(k," give right");
 */
 outline(k," is release!");
 ReleaseMutex(mutex[k]);
 //outline(k," give left"); 
 outline(k+1," is release!");
 ReleaseMutex(mutex[k+1]);
 //outline(k," give right");
 
}
/***************
等待吃饭
需要同时获得他两边的筷子
***************/
void wait_to_eat(int k)
{
 outline(k," is waiting...");
 /*
 EnterCriticalSection(fork + k);//获得左边的筷子
 outline(k," take left");
 EnterCriticalSection(fork + (k + 1)%NUM_OF_PH);//获得右边的筷子
 outline(k," take right");
 */
 //同时等待两根筷子
 WaitForMultipleObjects(2,mutex + k,true,INFINITE);
 outline(k," get two");
}
/********************
//没有竞争条件的输出函数
********************/
void outline(int who,const char *str)
{
 EnterCriticalSection(&crout);
 cout<<"process "<<who<<str<<endl;
 LeaveCriticalSection(&crout);
}

如果,你也跟我一样,头回看像EnterCriticalSection等等,这么长的函数名头大的话,我下面也小总结了一下上面用的的关于线程的函数功能和参数说明。

调试多线程的时候的一些问题:
project   Setting   ->   C++   ->Category中选Code   Generate   ->using   runtime   lib   下选多线程模式

project   setting   ->   c/c++   ->   Code   Generator   ->   use   run-time   library   ->   Mulitthreaded   DLL    

1._beginthreadex()或CreateThread()或AfxBeginThread()
都是用来创建线程的
_beginthreadex,AfxBeginThread都封装了CreateThread函数

2.VOID InitializeCriticalSection(
  LPCRITICAL_SECTION lpCriticalSection // critical section
  );
函数功能
  初始化一个临界资源对象
  参数说明
  lpCriticalSection 临界资源对象指针
  返回值
  没有

typedef RTL_CRITICAL_SECTION CRITICAL_SECTION;

typedef struct _RTL_CRITICAL_SECTION {
    PRTL_CRITICAL_SECTION_DEBUG DebugInfo;

    //
    //  The following three fields control entering and exiting the critical
    //  section for the resource
    //

    LONG LockCount;
    LONG RecursionCount;
    HANDLE OwningThread;        // from the thread's ClientId->UniqueThread
    HANDLE LockSemaphore;
    DWORD SpinCount;
} RTL_CRITICAL_SECTION, *PRTL_CRITICAL_SECTION;

3.DWORD WaitForMultipleObjects(  DWORD nCount,  const HANDLE* lpHandles,  BOOL bWaitAll,  DWORD dwMilliseconds);
其中参数

nCount 句柄的数量 最大值为MAXIMUM_WAIT_OBJECTS(64)
HANDLE 句柄数组的指针。
HANDLE 类型可以为(Event,Mutex,Process,Thread,Semaphore )数组
BOOL bWaitAll 等待的类型,如果为TRUE 则等待所有信号量有效在往下执行,FALSE 当有其中一个信号量有效时就向下执行
DWORD dwMilliseconds 超时时间 超时后向执行。 如果为WSA_INFINITE 永不超时。如果没有信号量就会在这死等

4.多个线程操作相同的数据时,一般是需要按顺序访问的,否则会引导数据错乱,无法控制数据,变成随机变量。为解决这个问题,就需要引入互斥变量,让每个线程都按顺序地

访问变量。这样就需要使用EnterCriticalSection和LeaveCriticalSection函数。
函数EnterCriticalSection和LeaveCriticalSection声明如下:
WINBASEAPI
VOID
WINAPI
EnterCriticalSection(
    __inout LPCRITICAL_SECTION lpCriticalSection
    );
   
WINBASEAPI
VOID
WINAPI
LeaveCriticalSection(
    __inout LPCRITICAL_SECTION lpCriticalSection
    );
lpCriticalSection是创建临界区对象。

5.HANDLE   CreateMutex(  
   LPSECURITY_ATTRIBUTES   lpMutexAttributes,   //   安全属性指针  
   BOOL   bInitialOwner,   //   初始拥有者  
   LPCTSTR   lpName   //   互斥对象名  
  );

当然,这些小小的例子可能体现不出什么编程实质来,但是能帮我们理清楚一下线程的概念,如果你有读过内核,那么,两者合起来,既知道怎么使,还知道怎么实现的,不是更好嘛。

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值