由于目前的MANGOS只是針對個(gè)人單機(jī)用戶制作的,并非真正的服務(wù)器版。使用的是單線程的處理方式。
關(guān)于mangos多線程處理方式
聽說配置這個(gè)文件是管理線程的,不過不知道有沒有用!
# 在多線程系統(tǒng) 使用線程MASK(只在Windows系統(tǒng)下使用)
# 默認(rèn): 0 (操作系統(tǒng)來選擇)
# 舉例: 數(shù)字
UseProcessors = 0
目前的mangos上了幾十人后,只要有組隊(duì)的組團(tuán)的就會(huì)卡,奇怪CPU占用也不高。請教了大大之后明白原來是mangos自身的瓶頸問題。
CK說:
由于目前的MANGOS只是針對個(gè)人單機(jī)用戶制作的,并非真正的服務(wù)器版。使用的是單線程的處理方式。這個(gè)由代碼就可以看出,整個(gè)World(包括人物,怪等)都是靠World.cpp里的update函數(shù)發(fā)起掉用的。而他是使用單線程的方式,從頭遍歷所有的玩家,一個(gè)一個(gè)按照次序的來進(jìn)行獲取封包,并處理然后發(fā)送。這也就是為什么,當(dāng)你打開MANGOS這個(gè)服務(wù)端,竟然CPU占用很少的原因。
在網(wǎng)上,我也看到過有人打算用多開區(qū)(EXE)來達(dá)到玩家分流,也只是治表不治本的方法。按照MANGOS的框架總體上來說人數(shù)達(dá)到100其實(shí)是一個(gè)上限值再上去的話,可能情況就是PING值雖然不高,但還是覺得卡。因?yàn)橥婕曳獍鼪]有及時(shí)處理,而停留在“等待處理”中。卡怪,卡魔法這樣的情況很容易產(chǎn)生。特別當(dāng)有一個(gè)PING值非常高的玩家,在SESSION排隊(duì)列表里比你靠前時(shí),這種情況最容易發(fā)生。
CK給出的代碼:
在World.cpp文件里,做一個(gè)線程函數(shù)來代替
update函數(shù)里的
CODE:
for (SessionMap::iterator itr = m_sessions.begin(), next; itr != m_sessions.end(); itr = next)
{
next = itr;
next++;
if(!itr->second)
continue;
if(!itr->second->Update(diff))
{
delete itr->second;
m_sessions.erase(itr);
}
}
[url=javascript:][Copy to clipboard][/url]
以上代碼就是我剛才說的,從頭遍歷所有玩家并依次調(diào)用,所有玩家session的update函數(shù)。以下是修正建立自己的線程
CODE:
DWORD World::_UpdateThread(LPVOID lp)
{
ThreadParm *parm = (ThreadParm *)lp;
World *pworld = (World *)parm->world;
HANDLE m_hSingle = NULL;
while (true)
{
SessionMap sessions = pworld->GetSessions();
SessionMap::iterator itr = NULL;
SessionMap::iterator next = NULL;
for (itr = sessions.begin(), next; itr != sessions.end(); itr = next)
{
next = itr;
next++;
if(itr == NULL || !itr->second || itr->second->GetWorking())
continue;
m_hSingle = OpenEvent(EVENT_ALL_ACCESS,true,"worldsession"); //這里必須對所操作資源進(jìn)行同步處理,否則將會(huì)出現(xiàn)線程之間資源訪問的沖突。一個(gè)線程處理一個(gè)玩家,其他線程直接跳轉(zhuǎn)到后面的列隊(duì)
if (m_hSingle == NULL)
{
m_hSingle = CreateEvent(NULL, FALSE, TRUE, "worldsession");
}
if (WaitForSingleObject(m_hSingle, 10000) == WAIT_TIMEOUT)//我把超時(shí)設(shè)置為10秒,以防死鎖
{
SetEvent(m_hSingle);
m_hSingle = CreateEvent(NULL, FALSE, TRUE, "worldsession");
}
itr->second->SetWorking(true);
SetEvent(m_hSingle); //記得別忘了把鎖打開,否則這個(gè)玩家之后所有的封包操作將被忽略。
if (!itr->second->Update(time(NULL)))
{
pworld->RemoveErrorSession(itr->second->GetAccountId());
}
else
{
itr->second->SetWorking(false);
}
}
Sleep(100); //線程間隔時(shí)間我設(shè)置為0.1秒
}
return 0;
}
[url=javascript:][Copy to clipboard][/url]
接下來就是如何去開啟線程進(jìn)行處理了,在world.cpp里有個(gè)SetInitialWorldSettings函數(shù),這個(gè)是初始化World里面所有數(shù)據(jù)的總?cè)肟凇N覀兛梢园?u>線程啟動(dòng)放那里。
先在mangosd.conf文件里設(shè)置一串
CODE:
WorldSessionThread = 3
[url=javascript:][Copy to clipboard][/url]
設(shè)置3個(gè)啟動(dòng)線程
接下去就是在SetInitialWorldSettings里修改了
CODE:
....
sLog.outString( "Loading Loot Tables..." );
LoadLootTables();
//在這里添加我們的線程函數(shù)
for (int i = 0; i diff = i;
t->world = this;
::CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)_UpdateThread,(LPVOID)t,0,&tid);
}
[url=javascript:][Copy to clipboard][/url]
GetIntDefault函數(shù)第一個(gè)我就不解釋了,第二個(gè)值是表示默認(rèn)值,比如你配置文件里沒設(shè)置WorldSessionThread = 3這條語句的話,默認(rèn)就是2個(gè)線程。
這樣一來,我們的MANGOS服務(wù)端在處理能力上,將得到很大的提升
龍?jiān)返牧直探o出的提示:
thread.cpp
#include <stdio.h>
#ifdef _WIN32
#include "socket_include.h"
#else
#include <unistd.h>
#endif
#include "Thread.h"
#ifndef __GNUC__
// UQ1: warning C4311: 'type cast' : pointer truncation
#pragma warning(disable:4311)
#endif
Thread::Thread(bool release)
:m_thread(0)
,m_running(true)
,m_release(false)
{
#ifdef _WIN32
m_thread = ::CreateThread(NULL, 0, StartThread, this, 0, &m_dwThreadId);
#else
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED);
if (pthread_create(&m_thread,&attr,StartThread,this) == -1)
{
perror("Thread: create failed");
SetRunning(false);
}
// pthread_attr_destroy(&attr);
#endif
m_release = release;
}
Thread::~Thread()
{
// while (m_running || m_thread)
if (m_running)
{
SetRunning(false);
SetRelease(true);
#ifdef _WIN32
struct timeval tv;
tv.tv_sec = 0;
tv.tv_usec = 100000;
select(0,NULL,NULL,NULL,&tv);
::CloseHandle(m_thread);
#else
sleep(1);
#endif
}
}
threadfunc_t STDPREFIX Thread::StartThread(threadparam_t zz)
{
Thread *pclThread = (Thread *)zz;
while (pclThread -> m_running && !pclThread -> m_release)
{
#ifdef _WIN32
struct timeval tv;
tv.tv_sec = 0;
tv.tv_usec = 100000;
select(0,NULL,NULL,NULL,&tv);
#else
sleep(1);
#endif
}
if (pclThread -> m_running)
{
pclThread -> Run();
}
pclThread -> SetRunning(false); // if return
return (threadfunc_t)zz;
}
bool Thread::IsRunning()
{
return m_running;
}
void Thread::SetRunning(bool x)
{
m_running = x;
}
bool Thread::IsReleased()
{
return m_release;
}
void Thread::SetRelease(bool x)
{
m_release = x;
}