从前面介绍的GAME类主体实现函数可以看出,整个游戏过程处于一个无限循环的过程中,为了保证游戏数据实时保存在服务端,在每一轮循环中都需要将更新的数据进行发送,这里本来可以直接在playgame函数体最后加上senddata函数实现这个功能,但为了学习应用下多线程技术,同时也可以将游戏进行过程中的通信部分与游戏部分分离开,以防止互相干扰,提高效率,因此新创建了一个线程专门用于发送游戏数据到服务端,定义线程函数:
HANDLE hSemaphore,h_event1,h_event2;
DWORD dwThreadId;
HANDLE hThread;
DWORD WINAPI ThreadFunc(LPVOID lpParam)
{
DWORD waitresult;
Game* temp = (Game*)lpParam;
while (1) {
if ((waitresult = WaitForSingleObject(hSemaphore, 0))==WAIT_OBJECT_0)
break;
WaitForSingleObject(h_event2, INFINITE);
ResetEvent(h_event2);
temp->senddata();
SetEvent(h_event1);
}
return 0;
}
在游戏main函数中加入如下代码:
h_event1 = CreateEvent(NULL, true, false, nullptr);
h_event2 = CreateEvent(NULL, true, false, nullptr);
hSemaphore = CreateSemaphore(NULL, 0, 1, NULL);//创建信号
hThread = CreateThread(NULL, 0, ThreadFunc, (void*)&mygame, 0, &dwThreadId);
plyagame函数首端加入以下前两行代码,sleep前加入最后一行代码:
WaitForSingleObject(h_event1, INFINITE);
ResetEvent(h_event1);
SetEvent(h_event2);
在最初的多线程实现中,并未加入关于事件的代码,导致游戏运行过程中不时会报错,主要时由于多线程不同步的问题导致的,主线程部分会不断的改变snake类和相关类的数据,而senddata函数则需要读取相应的数据,如果在读取过程中,恰好主线程将相应数据进行了删除,则将出现读取不到数据的错误,因此之后加入了多线程同步的相关代码;
多线程同步的实现方法有很多,这里主要采用了创建两个事件的方式进行,通信的线程在运行时,会等待主线程将event2激活,而主线程在运行到sleep之前时会激活event2,这样在主线程挂起时通信线程完成发送数据的操作,在通信良好的情况下,通信线程在主线程挂起期间完成发送数据并激活event1,这样主线程在进入下一轮循环时,接受到event1被激活的信号从而继续运行下面的代码,而若网络不好,则主线程会陷入停滞,这里还可以设置主线程等待事件,若等待时间过长则转入与用户相互的代码,这部分代码比较容易也就没有再写了。
同时另外定义了一个semaphore,当游戏结束时该信号被激活,通知通信线程游戏已结束,并跳出循环。
函数的最后就是关闭各类句柄的代码了:
if (!mygame.playagain()) {
SetEvent(h_event2);
ReleaseSemaphore(hSemaphore, 1, NULL);
CloseHandle(hSemaphore);
CloseHandle(hThread);
CloseHandle(h_event1);
CloseHandle(h_event2);
break;
}
至此,贪吃蛇网游化框架的主体搭建就全部完成了,整个框架还存在很多的不足,例如异常处理代码几乎全部被忽略,但出于只是复习所学知识的目的就不多做重复性工作了,下面贴几张游戏运行过程图: