使用python asyncio寫一個(gè)mmo(翻譯)

原文地址 https://7webpages.com/blog/writing-online-multiplayer-game-with-python-asyncio-getting-asynchronous/


你試過使用python的async么?這里我來告訴你如何做,我們來寫出一個(gè)可以用的例子 -- 一個(gè)可以多人玩的貪吃蛇.

  1. 介紹.
    MMO游戲毫無疑問是當(dāng)前主流趨勢,不管從技術(shù)角度還是從流行趨勢.曾幾何時(shí),寫一個(gè)MMO游戲是需要大量的預(yù)算和非常復(fù)雜的底層的編程技術(shù).最近,事情發(fā)生變化了.很多基于動(dòng)態(tài)語言的現(xiàn)在框架可以做到處理數(shù)以千計(jì)的玩家連接.(數(shù)以千計(jì),咋一聽起來有點(diǎn)挫啊,可是仔細(xì)想想一個(gè)單獨(dú)的進(jìn)程也差不多就是這個(gè)級別,大型的MMO都是在這個(gè)基礎(chǔ)上做多進(jìn)程擴(kuò)展).同時(shí)html 5和WebSockets標(biāo)準(zhǔn)能夠?qū)崿F(xiàn)網(wǎng)頁運(yùn)行的實(shí)時(shí)畫面游戲.
    python可能不是創(chuàng)建可擴(kuò)展的非阻塞服務(wù)器的最流行的工具,尤其和node.js對比.但是最新版本的python瞄準(zhǔn)了這一問題.asyncio和async/await能夠是異步的語言看起來和常規(guī)的代碼一樣直接.所以我們來通過這些新特性來演示一下如何創(chuàng)建一個(gè)MMO游戲.
  2. 變成異步
    一個(gè)gameserver能夠處理大量玩家的并行連接,同時(shí)要做到實(shí)時(shí).一個(gè)方法是創(chuàng)建線程,可是這種方法并不適用.運(yùn)行幾千個(gè)線程,cpu就要不停的切換線程(context switching),這樣就非常抵消.如果用多進(jìn)程呢?那更糟糕,因?yàn)樾枰加酶嗟膬?nèi)存.使用pyuthon還有一個(gè)更大的問題 - 通常python解釋器(CPython)設(shè)計(jì)上不能做到真正的多線程,它的目標(biāo)是單線程的效率. 這就是它使用GIL(global interpreter lock)的原因,這就使得python不同同時(shí)運(yùn)行代碼,來防止共享對象的使用. 一般情況下,解釋器切換到另一個(gè)線程發(fā)生在當(dāng)前線程正咋等待io或者其他.這確實(shí)能夠做到費(fèi)阻塞的io.因?yàn)橹蛔枞谝粋€(gè)線程里.然而,這并不能利用多線程的優(yōu)勢,因?yàn)椴荒軌蛲瑫r(shí)運(yùn)行代碼,即使是在多核cpu上面.其實(shí)呢, 在單線程里面也是完全可以做到非組賽io的,這樣就避免了大量的context-switching.
    這種單線程非阻塞的實(shí)現(xiàn)通過純python就可以做到.你需要的是select.你自己要寫一個(gè)事件循環(huán).這種方法需要你把邏輯都寫在一個(gè)地方, 然而你的應(yīng)用會很快變成非常復(fù)雜的狀態(tài)機(jī).有很多框架來簡化這個(gè)方法,最流行的是tornado和twisted.他們都是通過回調(diào)實(shí)現(xiàn)了非常復(fù)雜的協(xié)議.(有點(diǎn)像node.js)這些框架運(yùn)行自己的事件循環(huán),在特定事件發(fā)生時(shí)調(diào)用你定義的回調(diào).雖然這樣已經(jīng)非常好了,但是這個(gè)風(fēng)格是callback,代碼會變得脆弱(不至于吧).和這個(gè)相對應(yīng)的是同步代碼.為什么不在一個(gè)線程里做到這些呢?
    那么我就就要討論一個(gè)新概念, microthreads,(是叫微線程么).意思就是在單線程里面同步運(yùn)行很多任務(wù).當(dāng)你調(diào)用一個(gè)阻塞的任務(wù),背后的"manager"會運(yùn)行一個(gè)事件循環(huán).當(dāng)一個(gè)事件發(fā)生,這個(gè)manager就會通知等待這個(gè)事件的任務(wù).這個(gè)任務(wù)就繼續(xù)運(yùn)行直到遇到一個(gè)阻塞,然后有把運(yùn)行任務(wù)交給manager.

microthread 又叫做輕量級線程或者綠色線程.在這個(gè)偽線程中運(yùn)行的任務(wù)就叫做tasklets,greenlets或者協(xié)程.

python中的microthread最開始的實(shí)現(xiàn)是叫做Stackless Python.為什么這個(gè)會出名呢?因?yàn)?a target="_blank" rel="nofollow">EVE online這個(gè)游戲.
這個(gè)MMO游戲是發(fā)生在一個(gè)大宇宙,里面有數(shù)以千計(jì)的玩家發(fā)生不同的活動(dòng),都是實(shí)時(shí)的. stackless是一個(gè)獨(dú)立的python解釋器,這個(gè)解釋器去掉了標(biāo)準(zhǔn)的函數(shù)調(diào)用棧,直接控制流程,這樣contextswitch的代價(jià)最小.(沒看懂replaces standard function calling stack and controls the flow directly to allow minimum possible context-switching expenses. ). 雖然非常高效, 可是這個(gè)解釋器上能夠的lib就非常少. 我們看看eventlet和gevent,通過對標(biāo)準(zhǔn)io進(jìn)行修改,讓io把執(zhí)行權(quán)交給他們自己的內(nèi)部事件循環(huán).這樣就能夠把阻塞的代碼編程非阻塞的.這個(gè)方法的缺點(diǎn)是代碼上不太直觀.最新版的python介紹了一個(gè)原生的協(xié)程來作為生成器的高級形式.在python3.4, 引入了asyncio,依賴于原生協(xié)程,提供了單線程的同步.但是直到3.5才編程語言的一部分,使用了新的關(guān)鍵字async和await.下面是一個(gè)簡單的例子,解釋了asyncio運(yùn)行同步任務(wù)的情況:

import asyncio

async def my_task(seconds):
  print("start sleeping for {} seconds".format(seconds))
  await asyncio.sleep(seconds)
  print("end sleeping for {} seconds".format(seconds))

all_tasks = asyncio.gather(my_task(1), my_task(2))
loop = asyncio.get_event_loop()
loop.run_until_complete(all_tasks)
loop.close()    

我們觸發(fā)了兩個(gè)任務(wù),一個(gè)睡一秒一個(gè)睡兩秒.輸出如下:

start sleeping for 1 seconds
start sleeping for 2 seconds
end sleeping for 1 seconds
end sleeping for 2 seconds

as u can c,兩個(gè)任務(wù)并沒有阻塞住,第二個(gè)任務(wù)在第一個(gè)任務(wù)結(jié)束之前就開始了. 這是因?yàn)閍syncio.sleep是一個(gè)協(xié)程.下一次我們來看一下游戲的事件循環(huán).

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末超歌,一起剝皮案震驚了整個(gè)濱河市筒捺,隨后出現(xiàn)的幾起案子二拐,更是在濱河造成了極大的恐慌脱衙,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,657評論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件荞怒,死亡現(xiàn)場離奇詭異蹬铺,居然都是意外死亡昌跌,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,889評論 3 394
  • 文/潘曉璐 我一進(jìn)店門灵迫,熙熙樓的掌柜王于貴愁眉苦臉地迎上來秦叛,“玉大人,你說我怎么就攤上這事瀑粥≌醢希” “怎么了?”我有些...
    開封第一講書人閱讀 164,057評論 0 354
  • 文/不壞的土叔 我叫張陵狞换,是天一觀的道長避咆。 經(jīng)常有香客問我,道長修噪,這世上最難降的妖魔是什么查库? 我笑而不...
    開封第一講書人閱讀 58,509評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮黄琼,結(jié)果婚禮上樊销,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好现柠,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,562評論 6 392
  • 文/花漫 我一把揭開白布院领。 她就那樣靜靜地躺著,像睡著了一般够吩。 火紅的嫁衣襯著肌膚如雪比然。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,443評論 1 302
  • 那天周循,我揣著相機(jī)與錄音强法,去河邊找鬼。 笑死湾笛,一個(gè)胖子當(dāng)著我的面吹牛饮怯,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播嚎研,決...
    沈念sama閱讀 40,251評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼蓖墅,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了临扮?” 一聲冷哼從身側(cè)響起论矾,我...
    開封第一講書人閱讀 39,129評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎杆勇,沒想到半個(gè)月后贪壳,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,561評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡蚜退,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,779評論 3 335
  • 正文 我和宋清朗相戀三年闰靴,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片钻注。...
    茶點(diǎn)故事閱讀 39,902評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡蚂且,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出队寇,到底是詐尸還是另有隱情膘掰,我是刑警寧澤,帶...
    沈念sama閱讀 35,621評論 5 345
  • 正文 年R本政府宣布佳遣,位于F島的核電站识埋,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏零渐。R本人自食惡果不足惜窒舟,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,220評論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望诵盼。 院中可真熱鬧惠豺,春花似錦银还、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,838評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至热监,卻和暖如春捺弦,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背孝扛。 一陣腳步聲響...
    開封第一講書人閱讀 32,971評論 1 269
  • 我被黑心中介騙來泰國打工列吼, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人苦始。 一個(gè)月前我還...
    沈念sama閱讀 48,025評論 2 370
  • 正文 我出身青樓寞钥,卻偏偏與公主長得像,于是被迫代替她去往敵國和親陌选。 傳聞我的和親對象是個(gè)殘疾皇子理郑,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,843評論 2 354

推薦閱讀更多精彩內(nèi)容