关于mangos多线程处理方式
由于目前的MANGOS只是针对个人单机用户制作的,并非真正的服务器版。使用的是单线程的处理方式。 关于mangos多线程处理方式 听说配置这个文件是管理线程的,不过不知道有没有用! # 在多线程系统 使用线程MASK(只在Windows系统下使用) # 默认: 0 (操作系统来选择) # 举例: 数字 UseProcessors = 0 目前的mangos上了几十人后,只要有组队的组团的就会卡,奇怪CPU占用也不高。请教了大大之后明白原来是mangos自身的瓶颈问题。 CK说: 由于目前的MANGOS只是针对个人单机用户制作的,并非真正的服务器版。使用的是单线程的处理方式。这个由代码就可以看出,整个World(包括人物,怪等)都是靠World.cpp里的update函数发起掉用的。而他是使用单线程的方式,从头遍历所有的玩家,一个一个按照次序的来进行获取封包,并处理然后发送。这也就是为什么,当你打开MANGOS这个服务端,竟然CPU占用很少的原因。 在网上,我也看到过有人打算用多开区(EXE)来达到玩家分流,也只是治表不治本的方法。按照MANGOS的框架总体上来说人数达到100其实是一个上限值再上去的话,可能情况就是PING值虽然不高,但还是觉得卡。因为玩家封包没有及时处理,而停留在“等待处理”中。卡怪,卡魔法这样的情况很容易产生。特别当有一个PING值非常高的玩家,在SESSION排队列表里比你靠前时,这种情况最容易发生。 CK给出的代码: 在World.cpp文件里,做一个线程函数来代替 update函数里的 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] 以上代码就是我刚才说的,从头遍历所有玩家并依次调用,所有玩家session的update函数。以下是修正建立自己的线程 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"); //这里必须对所操作资源进行同步处理,否则将会出现线程之间资源访问的冲突。一个线程处理一个玩家,其他线程直接跳转到后面的列队 if (m_hSingle == NULL) { m_hSingle = CreateEvent(NULL, FALSE, TRUE, "worldsession"); } if (WaitForSingleObject(m_hSingle, 10000) == WAIT_TIMEOUT)//我把超时设置为10秒,以防死锁 { SetEvent(m_hSingle); m_hSingle = CreateEvent(NULL, FALSE, TRUE, "worldsession"); } itr->second->SetWorking(true); SetEvent(m_hSingle); //记得别忘了把锁打开,否则这个玩家之后所有的封包操作将被忽略。 if (!itr->second->Update(time(NULL))) { pworld->RemoveErrorSession(itr->second->GetAccountId()); } else { itr->second->SetWorking(false); } } Sleep(100); //线程间隔时间我设置为0.1秒 } return 0; } [url=javascript:][Copy to clipboard][/url] 接下来就是如何去开启线程进行处理了,在world.cpp里有个SetInitialWorldSettings函数,这个是初始化World里面所有数据的总入口。我们可以把线程启动放那里。 先在mangosd.conf文件里设置一串 CODE: WorldSessionThread = 3 [url=javascript:][Copy to clipboard][/url] 设置3个启动线程 接下去就是在SetInitialWorldSettings里修改了 CODE: .... sLog.outString( "Loading Loot Tables..." ); LoadLootTables(); //在这里添加我们的线程函数 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函数第一个我就不解释了,第二个值是表示默认值,比如你配置文件里没设置WorldSessionThread = 3这条语句的话,默认就是2个线程。 这样一来,我们的MANGOS服务端在处理能力上,将得到很大的提升 龙苑的林碧给出的提示: 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; } |