对于线程的概念,可能也就只是停留在概念的基础上吧,偶尔想起哲学家吃饭问题,闲来无心,找到了一段代码(自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 // 互斥对象名
);
当然,这些小小的例子可能体现不出什么编程实质来,但是能帮我们理清楚一下线程的概念,如果你有读过内核,那么,两者合起来,既知道怎么使,还知道怎么实现的,不是更好嘛。