之前因?yàn)闀r(shí)間匆忙,大區(qū)里產(chǎn)生唯一ID只是簡(jiǎn)單寫了一個(gè)方法,今天趁著有時(shí)間,把里面一些過程寫出來(lái),也算是個(gè)總結(jié)。 首先說(shuō)說(shuō)需求,現(xiàn)在游戲里數(shù)據(jù)庫(kù)為了分散熱點(diǎn),都采用分表方式,以前那種一張表AUTO_INCREMENT的方式顯然不行了,那么將唯一ID放到業(yè)務(wù)進(jìn)程里可行嗎?這個(gè)也不好,因?yàn)楝F(xiàn)在后臺(tái)都是多個(gè)部署,每個(gè)進(jìn)程可以保證ID唯一,但是存到數(shù)據(jù)庫(kù)的時(shí)候就可能重復(fù),所以我們還是得在DB上做文章。 因此就有了上篇文章的解決方案,單獨(dú)一張表,這張表的作用就是專門產(chǎn)生唯一ID,而且為多個(gè)業(yè)務(wù)提供唯一ID,多進(jìn)程情況下也不需要鎖表,效率比較高,是不是很像設(shè)計(jì)模式里的工廠。接著我來(lái)分析下這張表。我們?cè)賮?lái)看看表結(jié)構(gòu)和用法
create table SeqTab (
iSeqNo bigint(20) not null default 0, //表示唯一ID
iSeqType int(11) not null default 0, //表示業(yè)務(wù)ID
primary key(iSeqNo,iSeqType));
insert into SeqTab values(0,1); //初始化,假設(shè)一個(gè)業(yè)務(wù)編號(hào)為1
update SeqTab set iSeqNo=last_insert_id(iSeqNo+1) where iSeqType=1;
select last_insert_id(); //這兩句是獲取一個(gè)業(yè)務(wù)的唯一ID,供業(yè)務(wù)進(jìn)程使用。 其實(shí)說(shuō)到這張表,關(guān)鍵是說(shuō)LAST_INSERT_ID()這個(gè)方法。它有兩種形式LAST_INSERT_ID(),LAST_INSERT_ID(expr)。 這個(gè)東西的特點(diǎn)是,1.屬于每個(gè)CONNECTION,CONNECTION之間相互不會(huì)影響 2.不屬于某個(gè)具體的表 3.返回最后一次INSERT AUTO_INCREMENT的值 4.假如以此使用INSERT插入 多行數(shù)據(jù),只返回第一行數(shù)據(jù)產(chǎn)生的值 5.如果你UPDATE某個(gè)AUTO_INCREMENT的值,不會(huì)影響LAST_INSERT_ID()返回值 LAST_INSERT_ID(expr)與LAST_INSERT_ID()稍有不同,首先它返回expr的值,其次它的返回值會(huì)記錄在LAST_INSERT_ID()。 我們這里主要使用到了第一和第二個(gè)特點(diǎn),每個(gè)進(jìn)程并發(fā)執(zhí)行update SeqTab set iSeqNo=last_insert_id(iSeqNo+1) where iSeqType=1;,就獲取屬于進(jìn)程自己的iSeqNo并且記錄在 LAST_INSERT_ID中,通過第二句取出該值。 接著我們?cè)诒容^下其他一些辦法。 第一種是直接一張表,里面有一個(gè)ID字段,設(shè)置成AUTO_INCREMENT。這個(gè)的問題是每個(gè)業(yè)務(wù)ID不是連續(xù)的,是離散的。 第二種是使用GUID或者UUID,但是這個(gè)問題我個(gè)人覺得是效率上的差異,字符串沒有數(shù)字的效率好,另外數(shù)字ID今后也可以拼接一些區(qū)信息,之后跨區(qū)的時(shí)候可以方便獲取對(duì)象是哪個(gè)區(qū)的.
posted @
2012-12-06 23:01 梨樹陽(yáng)光 閱讀(2221) |
評(píng)論 (2) |
編輯 收藏
Mysql中除了使用auto_increment字段作為自增序號(hào)以外 還有另外一種辦法 可以提供唯一序列號(hào)并且可以由一張表完成對(duì)多個(gè)序列
要求的滿足。
大致如下:
1.創(chuàng)建一個(gè)簡(jiǎn)單表
create table SeqTab
( iSeqNo int not null default 0,
iSeqType int not null default 0);
2.插入一調(diào)記錄
insert into SeqTab values(0,13); // 0表示SeqNo初始值,13為SeqType,同一張表可對(duì)多個(gè)應(yīng)用提供Seq序列服務(wù)
3.不管多進(jìn)程 or 單進(jìn)程對(duì)SeqTab表同時(shí)進(jìn)行訪問,使用一下方法獲取序列號(hào)
1st->update SeqTab set iSeqNo=last_insert_id(iSeqNo+1) where iSeqType=13;
2nd->select last_insert_id();
4.因多進(jìn)程對(duì)SeqTab同時(shí)訪問,進(jìn)行update SeqTab set iSeqNo=last_insert_id(iSeqNo+1);時(shí),數(shù)據(jù)庫(kù)保證了update的事務(wù)
完整性,且last_insert_id()是與當(dāng)前mysql連接相關(guān)的,所以多進(jìn)程同時(shí)使用時(shí),也不會(huì)沖突。
posted @
2012-12-05 11:54 梨樹陽(yáng)光 閱讀(1713) |
評(píng)論 (2) |
編輯 收藏
相信大家在開發(fā)后臺(tái)的過程中都遇到過中文亂碼的問題,今天我就來(lái)講講其中的原因。 我這建了3張表,test_latin1,test_utf8,test_gbk,表結(jié)構(gòu)如下 +-------+----------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra |
+-------+----------+------+-----+---------+-------+
| name | char(32) | YES | | NULL | |
+-------+----------+------+-----+---------+-------+
我的前端是gbk的編碼
執(zhí)行下面的語(yǔ)句
set names 'latin1'
insert into test_latin1 set name='王';('王'字是GBK編碼)
select name from test_latin1;
結(jié)果是否為亂碼?
執(zhí)行下面的語(yǔ)句
set names 'gbk' insert into test_latin1 set name='王';('王'字是GBK編碼) select name from test_latin1; 結(jié)果是否為亂碼? 執(zhí)行下面的語(yǔ)句 set names 'latin1' insert into test_utf8 set name='王';('王'字是GBK編碼) select name from test_utf8 ; 結(jié)果是否為亂碼? 我們舉個(gè)例子,假設(shè)一個(gè)漢字的字符編碼為0xFFFF,它在屏幕上能夠正常顯示,如果漢字存入數(shù)據(jù)庫(kù)的時(shí)候和從數(shù)據(jù)庫(kù)中取出的時(shí)候,編碼一致,那么它肯定不是亂碼。反過來(lái),如果輸出的時(shí)候是亂碼,那么它肯定被轉(zhuǎn)碼了,至于為什么被轉(zhuǎn)碼了,我們得看看mysql里面做了什么(mysql難道會(huì)把無(wú)碼片變成了有碼片?) 首先mysql里面有2個(gè)概念,一個(gè)叫character set,一個(gè)叫collation。我們先說(shuō)說(shuō)character set。字符集就是數(shù)字,英文字符,漢字等編碼格式,我們常見的是utf8,gbk,gb2312。mysql里面比較復(fù)雜,有4個(gè)東西跟它有關(guān),分別是character_set_client,character_set_connection,character_set_database,character_set_results。set names (latin1)其實(shí)就是character_set_client=latin1,character_set_connection=latin1,character_set_results=latin1,它的流程是character_set_client ==> character_set_connection ==> Table Character ==> character_set_results。 我們按照上面的流程,來(lái)分析第一個(gè)問題。 set names 'latin1'----執(zhí)行了character_set_client=latin1,character_set_connection=latin1,character_set_results=latin1; insert into test_latin1 set name='王';這句話,mysql做了什么事呢?首先,character_set_client,它會(huì)把王字的編碼當(dāng)成latin1的編碼傳遞給character_set_connection(此時(shí)不會(huì)轉(zhuǎn)碼),character_set_connection會(huì)把編碼傳遞給Table Character,因?yàn)楸肀旧硎莑atin1,所以此時(shí)也不需要轉(zhuǎn)碼,select name from test_latin1;mysql會(huì)把test_latin1中的編碼傳遞給前端,此時(shí)也不需要轉(zhuǎn)碼,所以,走個(gè)流程下來(lái),我們輸入的是什么編碼,輸出的還是相同的編碼,因此,第一個(gè)問題的答案是不會(huì)是亂碼。我畫個(gè)流程圖latin1==>latin1==>latin1==>latin1,沒有轉(zhuǎn)碼的過程 我們?cè)趤?lái)看第二個(gè)問題。 set names 'test_gbk'----執(zhí)行了character_set_client=gbk,character_set_connection=gbk,character_set_results=gbk; insert into test_latin1 set name='王';character_set_client,它會(huì)把王字的編碼當(dāng)成gbk的編碼傳遞給character_set_connection(此時(shí)不會(huì)轉(zhuǎn)碼),character_set_connection會(huì)把編碼傳遞給Table Character,因?yàn)楸硎莑anti1的編碼格式,這個(gè)過程的時(shí)候就會(huì)進(jìn)行轉(zhuǎn)碼,但是latin1的字符集小于gbk的字符集,所以它會(huì)找不到對(duì)應(yīng)字符的編碼,此時(shí)會(huì)以?代替。select name from test_latin1,此時(shí)會(huì)從latin1轉(zhuǎn)碼成gbk,但是此時(shí)latin1已經(jīng)是錯(cuò)誤的數(shù)據(jù)了,所以得到的gbk編碼也是錯(cuò)誤的了。流程gbk==>gbk==>latin1==>gbk,其中g(shù)bk==>latin1出了問題,我們select出來(lái)的數(shù)據(jù)也就不可能是輸入時(shí)候的數(shù)據(jù)了。因此,這個(gè)問題的答案是亂碼。 第三個(gè)。 set names 'test_latin1' insert into test_utf8 set name='王';character_set_client,它會(huì)把王字的編碼當(dāng)成latin1的編碼傳遞給character_set_connection(此時(shí)不會(huì)轉(zhuǎn)碼),character_set_connection會(huì)把編碼傳遞給Table Character,此時(shí)表是utf8的格式,因此會(huì)進(jìn)行轉(zhuǎn)碼,latin1==>utf8,因?yàn)閡tf8的字符集>latin1字符集,因此,轉(zhuǎn)碼正常。select name from test_utf8;會(huì)從utf8轉(zhuǎn)碼成latin1,此時(shí)可以轉(zhuǎn)碼成功,因此我們最終得到的和輸入的時(shí)候是一致的,因此答案不是亂碼。流程latin1==>latin1==>utf8==>latin1,從小的字符集到大的字符集再到小的字符集,轉(zhuǎn)碼是不會(huì)有問題的。 屁話了這么多,無(wú)非想告訴大家一個(gè)萬(wàn)精油方法,表創(chuàng)建的字符集和set names都設(shè)置成同一個(gè)字符集,就基本可以滿足輸入數(shù)據(jù)不會(huì)在轉(zhuǎn)換過程中失真,也就是說(shuō)輸入是什么,輸出就是什么。建議有中文的都設(shè)置成utf8字符集,一勞永逸。
posted @
2012-11-26 19:56 梨樹陽(yáng)光 閱讀(2538) |
評(píng)論 (2) |
編輯 收藏
這是Riot的Design Director Tom Cadwell專門為中國(guó)玩家寫的講解匹配系統(tǒng)工作原理的帖子。
同時(shí)為了讓大家更好的理解匹配系統(tǒng),如果您覺得您遇到了特別不公平的匹配,請(qǐng)回復(fù)游戲開始時(shí)間和比賽結(jié)束截圖,我們會(huì)調(diào)查該局匹配是如何完成的,坑爹的玩家是為何加入到這一局的。
很多人抱怨看不懂,我來(lái)個(gè)精簡(jiǎn)比喻版的:
有個(gè)籃球聯(lián)盟,有無(wú)數(shù)個(gè)球員和大概20個(gè)等級(jí)的聯(lián)賽。
所有球員都是10級(jí)聯(lián)賽的成員,他們自由組合互相比賽,贏的人,升級(jí)到11級(jí)聯(lián)賽,輸?shù)娜私档?級(jí)聯(lián)賽。
然后每個(gè)等級(jí)聯(lián)賽再次開賽,又有的人升級(jí)有的人降級(jí),最終這20級(jí)的聯(lián)賽都有球員參加。
我們的大量的數(shù)據(jù)證明,一個(gè)球員的水平,會(huì)讓其穩(wěn)定在大約3個(gè)聯(lián)賽之間,也就是科比是參加20級(jí)聯(lián)賽的,且當(dāng)他和4個(gè)17級(jí)聯(lián)賽的人組隊(duì),基本不會(huì)輸給17級(jí)聯(lián)賽的人。且,把科比降到10級(jí)聯(lián)賽,他會(huì)輕松的在20局之內(nèi)回到20級(jí)。
理想情況下,球員都是在跟自己同樣經(jīng)歷的球員玩,一個(gè)中等水平玩家完全不會(huì)匹配到科比,科比也不會(huì)匹配到剛玩游戲的玩家。
事實(shí)上匹配系統(tǒng)的分級(jí)會(huì)比這個(gè)更復(fù)雜更智能,采用的是國(guó)際象棋所采用的elo系統(tǒng)。
再增加個(gè)FAQ:
Q:系統(tǒng)為了保持勝率50%,是否會(huì)在我連勝后故意塞給我一些菜隊(duì)友讓我輸?
A:系統(tǒng)的目的不是為了保持你的勝率,而是讓水平差不多的玩家一起玩。當(dāng)你和水平差不多的玩家一起玩時(shí)勝率會(huì)趨近50%,所以,系統(tǒng)是不會(huì)故意坑你的。
Q:我才100勝,為什么系統(tǒng)老匹配600勝的玩家給我?
A:勝場(chǎng)并不能反應(yīng)一個(gè)人的水平。如果把匹配系統(tǒng)比作跑步,練習(xí)了3年才能跑進(jìn)11秒的和第一次就跑進(jìn)11秒的人我們是同等看待的。匹配系統(tǒng)基于水平而不是基于經(jīng)驗(yàn)。
Q:我勝率60%,為什么匹配40%勝率的隊(duì)友、60%勝率的對(duì)手給我?
A:勝率也不能反映水平。匹配系統(tǒng)不但要看你是否贏了,也要看你贏了誰(shuí)。就像war3的sky在職業(yè)圈勝率其實(shí)并不高,但是虐一般的玩家勝率是100%。同樣水平的玩家,會(huì)因?yàn)殡S機(jī)匹配到對(duì)手的關(guān)系,勝率會(huì)40%~60%不等。
Q:你說(shuō)水平差不多,為什么我覺得他們這么菜?
A:匹配系統(tǒng)提供的是公平的機(jī)會(huì),而未必是你理想的結(jié)果。我們能追求系統(tǒng)公正,但是無(wú)法預(yù)測(cè)玩家單局內(nèi)的表現(xiàn)。
系統(tǒng)100%匹配曼聯(lián)對(duì)陣皇馬,但是不能保證某一次曼聯(lián)不會(huì)4:0碾壓皇馬,且在這局中,C羅表現(xiàn)yts,完全就在拖后腿。或者曼聯(lián)也可能連勝皇馬3次之類的。但是,系統(tǒng)只會(huì)把曼聯(lián)去匹配皇馬而不會(huì)出現(xiàn)曼聯(lián)對(duì)陣中超深圳隊(duì)。具體到某一局是皇馬贏還是曼聯(lián)贏取決于那一場(chǎng)的排兵布陣,臨場(chǎng)發(fā)揮,以及戰(zhàn)術(shù)意圖。
如果這個(gè)坑爹玩家真的不在你的水平等級(jí),他就會(huì)一直坑隊(duì)友,一直輸,等級(jí)一直降低,這樣會(huì)讓他離開你的匹配范圍,讓他不再可以和你匹配到。根據(jù)我們的數(shù)據(jù),玩家的elo基本是穩(wěn)定在較小范圍內(nèi)的。這也就是深圳隊(duì)和皇馬的差距,也是中國(guó)國(guó)家隊(duì)能贏法國(guó)隊(duì),確永遠(yuǎn)打不進(jìn)世界杯的理由。
系統(tǒng)沒辦法給你完美隊(duì)友,玩家會(huì)因?yàn)楹芏嘣虬l(fā)揮不好:使用不會(huì)的英雄、打了不想打的位置、玩法風(fēng)格和隊(duì)友不夠搭配,前期不利想掛機(jī)等等。但是你和對(duì)方玩家遇到這種情況的概率是相同的,系統(tǒng)并不會(huì)偏袒任何一方。所以想要完美隊(duì)友,請(qǐng)和朋友組隊(duì),不過那樣你也會(huì)碰見更厲害的對(duì)手。
如果大家在如何鑒定玩家水平上有好的想法歡迎提,但是如果想要通過抱怨玩家游戲內(nèi)表現(xiàn)來(lái)證明匹配系統(tǒng)不公平,就是在和風(fēng)車決斗了。每個(gè)人的看法都不一樣的,系統(tǒng)判斷他和你在遇到同樣的隊(duì)友和對(duì)手時(shí)候,勝率差不多,這也是我們目前能做到最好的了。
以下是文章的正文。
概述:
匹配系統(tǒng)的目的如下,優(yōu)先級(jí)從高到低:
1、 保護(hù)新手不被有經(jīng)驗(yàn)的玩家虐;讓高手局中沒有新手。
2、 創(chuàng)造競(jìng)技和公平的游戲?qū)郑雇婕业挠螒驑啡ぷ畲蠡?/p>
3、 無(wú)需等待太久就能找到對(duì)手進(jìn)入游戲。
匹配系統(tǒng)盡其所能的匹配水平接近的玩家,玩家的水平是來(lái)自他們?cè)诖酥摆A了誰(shuí)以及他們對(duì)手的水平。當(dāng)你戰(zhàn)勝對(duì)手,系統(tǒng)會(huì)認(rèn)為你更強(qiáng),當(dāng)你輸給對(duì)手,系統(tǒng)會(huì)認(rèn)為你更弱。雖然這對(duì)于某一局游戲并不是那么的公平,但是長(zhǎng)期來(lái)看,對(duì)于多局游戲是相當(dāng)?shù)墓剑阂驗(yàn)楹玫耐婕铱倳?huì)對(duì)游戲結(jié)果造成正面的、積極的影響。我們使用了這樣一個(gè)方法測(cè)試:給水平高的玩家一個(gè)新帳號(hào),然后看他們游戲數(shù)局后的結(jié)果。我們通過大量的測(cè)試來(lái)證明了我們的想法。
并且,匹配系統(tǒng)知道預(yù)先組隊(duì)的玩家有一些優(yōu)勢(shì),如果你是預(yù)先組隊(duì),會(huì)給你一些更強(qiáng)的玩家。我們用一些非常巧妙的數(shù)學(xué)方法來(lái)解決預(yù)先組隊(duì)的玩家VS solo玩家的匹配公平問題。我甚至讓兩個(gè)數(shù)學(xué)博士來(lái)驗(yàn)證,他們都說(shuō)給力!
匹配是怎么完成的?
首先,系統(tǒng)將你放進(jìn)適當(dāng)?shù)钠ヅ涑乩?#8212;—根據(jù)游戲模式(匹配模式、排位solo/雙人、排位5人、其他模式等等)
然后,系統(tǒng)會(huì)嘗試將匹配池里的人分到更細(xì)的匹配池里——5人組隊(duì) VS 5人組隊(duì),低等級(jí)新手 vs 其他一些低等級(jí)新手,如此這般。
當(dāng)你在匹配池中,系統(tǒng)會(huì)開始嘗試找到合適的配對(duì),目標(biāo)是撮合一個(gè)雙方獲勝機(jī)會(huì)都為50%的游戲。
第1步:確定你的實(shí)力:
*如果你是solo,就直接使用你的個(gè)人匹配分(也就是elo值,匹配模式和排位賽有不同的匹配分)
*如果你是預(yù)先組隊(duì)的,你的匹配分是你隊(duì)伍的平均分,并且會(huì)根據(jù)你組隊(duì)的規(guī)模稍微提高一些,這樣才能保證你匹配到更強(qiáng)的對(duì)手來(lái)抵消你組隊(duì)的優(yōu)勢(shì)。我和一個(gè)計(jì)算機(jī)生物學(xué)的博士(Computational Biology Ph.D)通過研究成百上千的游戲結(jié)果,計(jì)算出了預(yù)先組隊(duì)到底有多大的優(yōu)勢(shì)。我們還在幕后做了一些其他調(diào)整,比如新手和高玩組隊(duì),比如某地圖上藍(lán)隊(duì)和紫隊(duì)的玩家哪個(gè)更有優(yōu)勢(shì),諸如此類。
第2步:確定你合適的對(duì)手:
*首先,系統(tǒng)會(huì)基于你的elo值,給你匹配跟你非常相近的玩家。最終,系統(tǒng)會(huì)放寬匹配的條件,給你一些不是那么完美的匹配,因?yàn)槟憧隙ㄒ膊幌胗肋h(yuǎn)匹配不到人。
*新手會(huì)得到一些特殊的保護(hù),通常新手只會(huì)匹配到其他新手(在成熟的服務(wù)器里,這個(gè)比例達(dá)到了99%+。除非這個(gè)新手和一個(gè)高級(jí)玩家朋友預(yù)先組隊(duì))
第3步:確定匹配:
*最終,系統(tǒng)會(huì)匹配10個(gè)大體上同水平、同等級(jí)的玩家,促成一個(gè)游戲。
*系統(tǒng)會(huì)嘗試平衡這個(gè)隊(duì)伍,盡量使雙方的獲勝機(jī)會(huì)都為50%。在絕大多數(shù)時(shí)間,誤差會(huì)在3%之內(nèi)——類似50/50,49/51,48/52。實(shí)際上的獲勝機(jī)會(huì)會(huì)有一點(diǎn)點(diǎn)差別(會(huì)在Q&A里面回答這個(gè)問題),但是我們的研究標(biāo)明,在絕大多數(shù)情況下,這實(shí)際上是一個(gè)非常精確的預(yù)測(cè)。
長(zhǎng)期來(lái)講,我的匹配分(Elo值)是如何被測(cè)量的?
我們使用了一個(gè)修改過的ELO系統(tǒng)。ELO系統(tǒng)的基本要點(diǎn)通過使用數(shù)學(xué)比較兩個(gè)人的積分,來(lái)預(yù)測(cè)兩人的比賽結(jié)果——類似“A和B比賽數(shù)局,A會(huì)贏掉75%的局”。
然后,比賽結(jié)果出來(lái)了。如果你贏了,你會(huì)加分,如果你輸了,你會(huì)被扣分。如果你是“出人意料”的贏了(系統(tǒng)認(rèn)為你輸?shù)目赡苄愿螅銜?huì)贏得更多的分?jǐn)?shù)。額外的,如果你是一個(gè)新玩家,你會(huì)加分減分更快,以便于你可以快速的進(jìn)入到你的水平等級(jí)。長(zhǎng)期來(lái)看,這意味著好的玩家會(huì)得到高的匹配分,因?yàn)樗麄兛偸浅^系統(tǒng)的預(yù)期,他們會(huì)不斷加分直到系統(tǒng)可以正確的預(yù)測(cè)他們的勝率。
我們修改這個(gè)系統(tǒng)給團(tuán)隊(duì)比賽使用,基本概念是:基于該團(tuán)隊(duì)的所有玩家,得到一個(gè)團(tuán)隊(duì)ELO值。如果你的隊(duì)伍勝利,系統(tǒng)會(huì)假設(shè)該隊(duì)伍的所有玩家都要比系統(tǒng)猜測(cè)的“更強(qiáng)”,并且加分。雖然有一些問題,但是總體上來(lái)講是有效的,特別是玩家預(yù)先組隊(duì)的時(shí)候。
舉例,本人在北美的服務(wù)器上有2000的普通匹配模式elo。如果我建一個(gè)小號(hào),就算沒有天賦和符文,我打到8級(jí)的時(shí)候就已經(jīng)有1800elo了。這個(gè)系統(tǒng)并不完美,但是確實(shí)能夠讓玩家快速的接近自己水平所在的位置。
當(dāng)你才開始玩的時(shí)候,我們也對(duì)ELO做一些微調(diào),讓你更快的進(jìn)入你水平所在的位置。
*我們有大量的,有優(yōu)先級(jí)的方法來(lái)鑒定一個(gè)玩家,相比一個(gè)標(biāo)準(zhǔn)的新玩家是否更有技巧,更猛。如果發(fā)現(xiàn)是的,我們會(huì)在幕后提高他的elo一個(gè)檔次。
*我們同樣也會(huì)分辨真的菜鳥新手。
*提升等級(jí)也會(huì)極大的提高你的elo值。這個(gè)也將幫助系統(tǒng)將30級(jí)滿級(jí)的召喚師和低等級(jí)的召喚師區(qū)分開來(lái)
如果你想知道ELO系統(tǒng)的理論,以及更多細(xì)節(jié),你可以看看這:
http://en.wikipedia.org/wiki/Elo_rating_system
http://zh.wikipedia.org/wiki/ELO
呃,等等,你是怎么處理組隊(duì)玩家 vs solo(單排)玩家的?
我們大多數(shù)情況下,會(huì)通過將5人組隊(duì)的隊(duì)伍匹配給另外一個(gè)5人組隊(duì)的隊(duì)伍來(lái)避免這種情況的發(fā)生(幾乎是所有情況下)。
對(duì)于“部分”組隊(duì),我們進(jìn)行了大量的研究,發(fā)現(xiàn)優(yōu)勢(shì)并沒有想象的那么大,所以我們也會(huì)把他們混到solo(單排)的玩家里。我們發(fā)現(xiàn)有大量的因素會(huì)影響到組隊(duì)優(yōu)勢(shì)的大小:從預(yù)先組隊(duì)的規(guī)模(比如2、3、4、5組隊(duì)),到組隊(duì)玩家的水平,到高玩帶菜鳥的組合,到玩家水平不同而導(dǎo)致的情況不同,以及其他的一些必須考慮到的微妙因素。這個(gè)要比一些我們?cè)娺^的點(diǎn)對(duì)點(diǎn)算法-將任意的統(tǒng)計(jì)數(shù)據(jù)雜糅在一起猜測(cè)分?jǐn)?shù)-要可靠的多
發(fā)現(xiàn)這些優(yōu)勢(shì),我們就知道對(duì)于預(yù)先組隊(duì)的隊(duì)伍,需要提高多少elo值,來(lái)達(dá)成一個(gè)公平的匹配,確定一個(gè)適當(dāng)?shù)模跀?shù)學(xué)上合理的調(diào)整。結(jié)果在有些情況下非常令人驚訝(同時(shí)會(huì)校正統(tǒng)計(jì)數(shù)據(jù))。
雖然我們不會(huì)給出精確的數(shù)值,因?yàn)檫@是商業(yè)機(jī)密,但是我們可以告訴您:
*5人組隊(duì)只是比5個(gè)路人稍強(qiáng)。
*部分組隊(duì)只是比5個(gè)路人略強(qiáng)。
*菜鳥5人組隊(duì)并不會(huì)帶來(lái)太大的優(yōu)勢(shì),但是高玩組隊(duì)會(huì)有很大的優(yōu)勢(shì)。
*團(tuán)隊(duì)實(shí)力方差高的隊(duì)伍,會(huì)比方差低的隊(duì)伍更強(qiáng)。(方差簡(jiǎn)單來(lái)說(shuō),是在平均值相同的情況下反應(yīng)各個(gè)元素的大小差異,方差大表示差異大,高方差的隊(duì)伍類似高玩帶低玩,低方差的隊(duì)伍各個(gè)隊(duì)員實(shí)力接近。)
*這說(shuō)明了大體上,高水平玩家的Carry作用(可以理解為帶領(lǐng)或者大腿),比低水平玩家的送人頭作用(feeder)要強(qiáng)力。
好吧…那為什么要把預(yù)先組隊(duì)的玩家和非組隊(duì)玩家匹配到一起?
這是一些原因:
*這會(huì)幫助系統(tǒng)更快的找到適合你的匹配分,讓系統(tǒng)更快的給你公平的匹配。這個(gè)的工作原理是,如果你組隊(duì),會(huì)減低運(yùn)氣所帶來(lái)的成分,如果你單排,你的隊(duì)友的好壞將對(duì)你輸贏的影響更大。如果你預(yù)先組隊(duì),你會(huì)和你水平差不多的玩家組成隊(duì)伍,你隨機(jī)遇到猛男/坑爹隊(duì)友幾率會(huì)更小。因?yàn)橛螒虻慕Y(jié)果更多來(lái)自你和水平相近的朋友的表現(xiàn),而不是隨機(jī)因素,所以你的匹配分會(huì)更快的到達(dá)精確的值。
*我們希望玩家可以和自己的朋友一起玩,因?yàn)檫@樣會(huì)讓他們玩的更有樂趣。你也不可能為5v5的游戲設(shè)置單獨(dú)的2人匹配池或者3人匹配池,你需要組合他們來(lái)讓系統(tǒng)工作。我們選擇包含5人組隊(duì),因?yàn)檫@非常有樂趣。如果我們以后有足夠大的匹配池,我們可能會(huì)將5人組隊(duì)和部分組隊(duì)區(qū)分開來(lái),但是數(shù)據(jù)告訴我們,這基本不會(huì)提升匹配的公平程度,兩者的效果基本相同。
其他一些常見的問題:
Q:為什么不加入一些其他的細(xì)節(jié),類似擊殺數(shù)等等來(lái)確定我的匹配分?
A:因?yàn)檫@是有偏差的,并且因?yàn)榉浅ky以給擊殺數(shù)這個(gè)數(shù)值來(lái)評(píng)分,你使用一個(gè)gank英雄的時(shí)候(類似老鼠和易大師),要?dú)⒍嗌偃瞬拍芩闶呛玫哪兀慷疫@會(huì)讓好的輔助玩家非常吃虧,因?yàn)樗麄兊哪康木筒皇悄萌祟^,甚至?xí)榱俗约旱腃arry擋死。最后,玩家會(huì)為了刷數(shù)據(jù),故意拖長(zhǎng)游戲時(shí)間,然后拿大量farm對(duì)方的人頭,而不是為了贏得比賽。我們盡量把測(cè)量玩家水平和激勵(lì)玩家的機(jī)制放到努力取勝上面,我們避免了一些不必要的周邊行為,而這些行為既沒樂趣,還會(huì)擾亂匹配系統(tǒng)。
Q:我非常憤怒,因?yàn)槠ヅ湎到y(tǒng)老給我坑爹隊(duì)友(feeders,送人頭的)。為什么不阻止這種情況發(fā)生?
A:我們的確有試圖阻止這種情況發(fā)生,但是如果你被匹配到一個(gè)明顯很弱的玩家,這也說(shuō)明匹配系統(tǒng)同時(shí)匹配給你了一個(gè)或者多個(gè)強(qiáng)力的玩家。根據(jù)我們的研究,我們發(fā)現(xiàn)Carry(大腿)對(duì)隊(duì)伍的帶領(lǐng)作用要比f(wàn)eeder(送人頭,坑爹)的坑爹作用更強(qiáng)。原因是在LOL里,多次擊殺同一個(gè)玩家的收益是會(huì)遞減的,并不像其他的同類游戲。我們的分析標(biāo)明,在平均elo相同的情況下,提高或者降低這個(gè)隊(duì)伍的某個(gè)玩家的elo值100(其他玩家相應(yīng)降低/提高以保持平均分相同),整個(gè)隊(duì)伍的實(shí)力會(huì)提高約7點(diǎn)elo值。這也表明,LOL中Carry的作用要比f(wàn)eeder的作用更給力一些。確實(shí),有時(shí)候你會(huì)因?yàn)槠ヅ涞絝eeder而輸?shù)暨@一局比賽,但是那是因?yàn)槟銈冴?duì)的Carry不夠給力。
Q:這樣的話,如果我連勝了數(shù)盤,我是不是會(huì)被匹配到一些完全不可戰(zhàn)勝的對(duì)手?
A:不全是。連勝導(dǎo)致你的匹配分會(huì)提高,你會(huì)不斷遇到更強(qiáng)的對(duì)手——但是我們并不是故意的讓你的勝率保持在50%的,我們的目的只是為了系統(tǒng)能夠正確的預(yù)測(cè)游戲結(jié)果。最終,你會(huì)達(dá)到你的極限,你將會(huì)大致保持50%的勝率。比平均水平高的玩家,往往勝率會(huì)比50%略高,因?yàn)楸人麄內(nèi)醯耐婕腋啵人麄儚?qiáng)的玩家更少。所以匹配時(shí),往往會(huì)略微“向下匹配”。對(duì)于排位頂尖的高端玩家,他們經(jīng)常會(huì)有90%的勝率。
Q:你們會(huì)如何設(shè)計(jì)固定的隊(duì)伍?類似WOW的競(jìng)技場(chǎng)隊(duì)伍?
A:這是一個(gè)非常好的想法,并且讓我們有機(jī)會(huì)設(shè)計(jì)出更好的匹配系統(tǒng)。我們遲早會(huì)做這個(gè),并且使用我們開發(fā)的新方法。我們需要檢驗(yàn)并且搞清楚你大體上有多強(qiáng)力(例如你的個(gè)人積分),同時(shí)允許你創(chuàng)建/解散隊(duì)伍。這是個(gè)非常大的工程,但是我們對(duì)此非常有激情~
Q:如果匹配系統(tǒng)真的那么公平,那為何我老遇見那種一邊倒的比賽?
A:有兩個(gè)原因。第一,LOL有時(shí)候“雪球效應(yīng)”會(huì)非常明顯。前期太差的表現(xiàn)會(huì)導(dǎo)致游戲讓人感覺非常一邊倒。特別是某些隊(duì)伍,如果他們開始很順風(fēng),就會(huì)一直很順風(fēng)。我們遇到過同樣的隊(duì)伍,第一局25-5取勝,第2局確以類似的比分輸?shù)簟5诙€(gè)原因是,玩家發(fā)揮的并不好,隊(duì)伍選取陣容也不好。要進(jìn)行一局勢(shì)均力敵的比賽,你需要平衡玩家水平和平衡陣容的選取。有時(shí)候玩家選了一個(gè)比較渣的陣容,比如5個(gè)近戰(zhàn)dps,或者3坦克2法師之類的,或者沒選打野英雄而對(duì)面有。這樣的話,盡管你的隊(duì)伍實(shí)力也很不錯(cuò),但是情況往往慘不忍睹。
Q:為什么我作為一個(gè)高等級(jí)玩家,有時(shí)候會(huì)匹配到一些低等級(jí)玩家?他們看上去都是來(lái)送人頭的。
A:當(dāng)一個(gè)高等級(jí)玩家和一個(gè)低等級(jí)玩家組隊(duì),這是一個(gè)非常令人頭疼的問題。我們希望玩家可以和自己的朋友一起玩,并且希望這是一種愉快的體驗(yàn)。但是我們并不希望將一部分人的快樂建立在另一部分人的痛苦之上,所以我們往往將這種組合評(píng)分更高,保護(hù)新玩家不會(huì)被高等級(jí)玩家虐待。非常不幸的是,不管我們?cè)趺醋觯覀儼堰@樣的組合匹配到任何的游戲中,都有可能造成不愉快的體驗(yàn)。因此,我們計(jì)劃將實(shí)施一個(gè)“不平衡組隊(duì)”的隊(duì)列,類似我們盡量將5人組隊(duì)匹配給5人組隊(duì)。
Q:我20級(jí)了,然后我被匹配到了一些10級(jí)的和一些29級(jí)的,怎么回事?
A:當(dāng)不同等級(jí)的玩家組隊(duì),我們會(huì)使用他們的平均等級(jí)來(lái)作為匹配的參考。等級(jí)并不是匹配系統(tǒng)的主導(dǎo)參數(shù)——匹配系統(tǒng)通常是使用實(shí)力來(lái)匹配——但是我們也會(huì)盡量將等級(jí)相近的玩家匹配到一起。在預(yù)先組隊(duì)的情況下,我們沒法替玩家選擇,所以我們盡我們所能,使用平均等級(jí)。我們會(huì)在這個(gè)計(jì)算系統(tǒng)里把30級(jí)的玩家看作36級(jí),所以我們通常能讓中等級(jí)玩家的游戲沒有30級(jí)玩家,然而有時(shí)候呢,29級(jí)玩家能插進(jìn)來(lái)。
posted @
2012-11-09 14:20 梨樹陽(yáng)光 閱讀(1613) |
評(píng)論 (1) |
編輯 收藏
記錄下這個(gè)小問題。root下可以顯示路徑但是切換到其他用戶時(shí)沒有路徑顯示,很不方便。在/etc/profile中加入export PS1='\u@\h:\w>',其中\(zhòng)u顯示當(dāng)前用戶賬號(hào),\h顯示當(dāng)前主機(jī)名,\W顯示當(dāng)前路徑。然后source一下就搞定了
posted @
2012-11-08 11:49 梨樹陽(yáng)光 閱讀(1389) |
評(píng)論 (0) |
編輯 收藏
開門見山,我先提出幾個(gè)問題,大家可以先想想,然后我再說(shuō)出我的方法
1.如何判斷一個(gè)數(shù)M是否為2的N次方?
2.一個(gè)數(shù)N,如何得到一個(gè)數(shù)是M,M是不小于N的最小2的K次方
先說(shuō)第一個(gè)問題,我有兩個(gè)思路
第一,可以通過判斷M的二進(jìn)制中1的個(gè)數(shù)。而判斷M中1的個(gè)數(shù)可以通過下面方法獲得
int GetOneCnt(int m)
{
if ( m == 0 )
return 0;
int cnt = 1;
while(m & (m-1))
{
cnt++;
m--;
}
return cnt;
}
很明顯M中1的個(gè)數(shù)為1和M是2的N次方互為沖要條件
第二個(gè)思路,我們可以這樣,還是利用M的二進(jìn)制表示,從最高位開始,以變量high_pos表示第一個(gè)1的下標(biāo),接著從最低位開始,變量low_pos表示第一個(gè)1的下標(biāo),如果high_pos=low_pos,則M為2的N次方
int HighestBitSet(int input)
{
register int result;
if (input == 0)
{
return -1;
}
#ifdef WIN32
_asm bsr eax, input
_asm mov result, eax
#else
asm("bsr %1, %%eax;"
"movl %%eax, %0"
:"=r"(result)
:"r"(input)
:"%eax");
#endif
return result;
}
int LowestBitSet(int input)
{
register int result;
if (input == 0)
{
return -1;
}
#ifdef WIN32
_asm bsf eax, input
_asm mov result, eax
#else
asm("bsf %1, %%eax;"
"movl %%eax, %0"
:"=r"(result)
:"r"(input)
:"%eax");
#endif
return result;
}
再說(shuō)第二個(gè)問題
其實(shí)有了第一個(gè)問題的思路,這個(gè)問題就更好解決了,先判斷一個(gè)數(shù)是否為2^N,如果是,直接返回,否則返回2^(N+1)
代碼如下
int CeilingPowerOfTwo(int iInput)
{
if (iInput <= 1)
return 1;
int32_t highestBit = HighestBitSet(iInput);
int32_t mask = iInput & ((1 << highestBit) - 1); // 相當(dāng)于input對(duì)2^highestBit求余
highestBit += ( mask > 0 );
return (1<<highestBit);
}
posted @
2012-10-01 15:53 梨樹陽(yáng)光 閱讀(1370) |
評(píng)論 (4) |
編輯 收藏
如果你對(duì)能很快回答出unicode和utf-8的關(guān)系,你可以直接跳過這篇文章。下面我來(lái)說(shuō)說(shuō)兩者的關(guān)系和轉(zhuǎn)換。(本文使用符號(hào)2字代表所有的漢字,英文,數(shù)字等)
首先明確一點(diǎn),UTF-8是UNICODE一種實(shí)現(xiàn)方式。
UNICODE:代表一種符號(hào)集合,它規(guī)定了一種符合的二進(jìn)制表示,沒有指明存儲(chǔ)方式。(
http://www.unicode.org/)
UTF-8:實(shí)現(xiàn)了UNICODE,使用多字節(jié)的存儲(chǔ)方式。
我們先來(lái)考慮幾個(gè)問題。
第一,如果使用單字節(jié)表示符號(hào),很明顯,完全不夠用
第二,如果使用多字節(jié)表示符號(hào),那么,機(jī)器在讀取的時(shí)候,它怎么知道3個(gè)字節(jié)表示一個(gè)符號(hào),還是表示3個(gè)符號(hào)
第三,如果使用2個(gè)字節(jié)表示一個(gè)符號(hào),首先,最多能表示65535個(gè)字符還是會(huì)不夠用,就算夠用,比如ASCII碼這類僅需1個(gè)字節(jié)就可以表示的符號(hào),用2個(gè)字節(jié)表示,浪費(fèi)空間了。
因此,UTF-8孕育而生。
首先UTF-8使用變長(zhǎng)表示符號(hào),簡(jiǎn)單的說(shuō),有的時(shí)候用1個(gè)字節(jié)表示符號(hào),有的時(shí)候用2個(gè)字節(jié)表示符號(hào),這樣解決了浪費(fèi)空間的問題。那么,如何解決第二個(gè)問題的呢,我們得了解下UFT-8的編碼規(guī)則。
1.對(duì)于單字節(jié)的符號(hào),字節(jié)第一個(gè)為0,后面7為為這個(gè)符號(hào)的unicode碼
2.對(duì)于N字節(jié)的符號(hào)(N>1),第一個(gè)字節(jié)前N位為1,第N+1位為0,后面字節(jié)的前兩位設(shè)為10,剩下可編碼的位,為該符號(hào)的UNICODE編碼。
這里我從網(wǎng)上找了一副圖
Unicode符號(hào)范圍 | UTF-8編碼方式 (十六進(jìn)制) | (二進(jìn)制)0000 0000-0000 007F | 0xxxxxxx0000 0080-0000 07FF | 110xxxxx 10xxxxxx0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
下面我具體解釋下這幅圖。
首先看第一行,它的意思是00000000到0000007F的UNICODE編碼,對(duì)應(yīng)的UTF-8的編碼方式為0XXXXXXX(X表示可編碼位,不足的補(bǔ)0)。
第二行表示00000080到000007FF的UNICODE編碼,對(duì)應(yīng)的UTF-8的編碼方式為110XXXXX 10XXXXXX。以此類推
那么,問題是,這個(gè)范圍是怎么定的?
很簡(jiǎn)單,我們還是從第一行說(shuō)起。007F,實(shí)際有效位只有7位,所以,0xxxxxxx就足矣。但是0800開始,有效位至少為8位,我們得增加一個(gè)字節(jié),按照UTF-8的規(guī)定,2字節(jié)的表示方式為110XXXXX 10XXXXXX,我們的編碼位為11位(X的個(gè)數(shù)),所以,我們最多可以表示UNICODE編碼位11位的字符,也就是07FF。07FF過了就是0800,有效位至少為12位,我們得用3字節(jié)來(lái)表示,按照UTF-8的規(guī)定,1110XXXX 10XXXXXX 10XXXXXX,最大編碼位為16位,也就是FFFF,最后一行我就不再解釋了。
通過上面這個(gè)過程我們了解了,UNICODE轉(zhuǎn)UTF-8的過程,當(dāng)然,逆過來(lái)就是UTF-8轉(zhuǎn)換成UNICODE。
我們通過一個(gè)例子來(lái)演示上面的過程。漢字“楊”,UNICODE的編碼位0x6768,二進(jìn)制形式為0110011101101000,根據(jù)上面的圖,我們知道它屬于第三行,因此,它應(yīng)該放入1110XXXX 10XXXXXX 10XXXXXX的模板中,結(jié)果是11100110 10011101 10101000,十六進(jìn)制表示為E69DA8。
另外設(shè)計(jì)編碼問題,我們繞不開另一個(gè)問題,就是大端小端的問題,不過這個(gè)問題,網(wǎng)上資料很多,也很好實(shí)踐,這里我就不多啰嗦了。
posted @
2012-09-23 22:56 梨樹陽(yáng)光 閱讀(1792) |
評(píng)論 (1) |
編輯 收藏
進(jìn)程間通信方式包括了管道,消息隊(duì)列,F(xiàn)IFO,共享內(nèi)存,而共享內(nèi)存是其中效率最高的。下圖解釋了其效率最高的原因(圖片截取自《UNIX網(wǎng)絡(luò)編程》)

我們可以看到上面的拷貝次數(shù)是4次,下面則是2次。
接下來(lái)我們看看使用,其實(shí)網(wǎng)上和書上都有了很多資料,API就是那么幾個(gè)。
int shmget(key_t key,size_t size,int shmflag)。
key:可以指定IPC_PRIVATE,那么系統(tǒng)會(huì)為你創(chuàng)建一個(gè)key,并且返回一個(gè)id號(hào)。也可以通過ftok函數(shù)生成一個(gè)key(不了解ftok的童鞋可以動(dòng)手man一下)。那么為什么要一個(gè)IPC既要一個(gè)key又有一個(gè)ID呢。這里我覺得是為了方便其他進(jìn)程訪問。進(jìn)程A創(chuàng)建了一個(gè)共享內(nèi)存,內(nèi)核為其分配一個(gè)ID,這個(gè)時(shí)候進(jìn)程B想要訪問,他怎么獲取這個(gè)ID(難道需要A把ID發(fā)送給B??)。但是我們用一個(gè)key就很方便了。事先A,B進(jìn)程都知道這個(gè)key,那么A創(chuàng)建了,B就可以通過事先知道key找到這塊內(nèi)存。
size:共享內(nèi)存的大小。如果是獲得一塊內(nèi)存,則該值應(yīng)該為0。
shmflag:讀寫權(quán)限的組合,可以與IPC_CREAT和IPC_EXCL按位或。當(dāng)指定一個(gè)key時(shí),IPC_CREAT和IPC_EXCL配合使用可以在存在該key的共享內(nèi)存時(shí)返回-1。
當(dāng)該函數(shù)調(diào)用成功后,返回一個(gè)系統(tǒng)分配的共享內(nèi)存,并且size大小的字節(jié)被初始化為0
void *shmat(int shmid, const void *shmaddr, int shmflg)
有了一塊共享內(nèi)存后,進(jìn)程需要映射該內(nèi)存到進(jìn)程的地址空間。這個(gè)函數(shù)就是作用。
shmid就是之前獲得ID,shmaddr如果指定了,就會(huì)配合shmflg確定映射地址,不過一般都不這么干的。返回值就是獲得的地址,你可以往里面寫你或者讀你需要的數(shù)據(jù)了。同時(shí)調(diào)用了該函數(shù),系統(tǒng)會(huì)修改shmid_ds數(shù)據(jù)。
int shmdt(const void *shmaddr)
解除綁定關(guān)系,參數(shù)就是我們之前獲取的那個(gè)返回地址。(其實(shí)我覺得這里參數(shù)如果為ID貌似更統(tǒng)一些吧)
int shmctl(int shmid, int cmd, struct shmid_ds *buf)
這個(gè)函數(shù)主要做一些修改和查詢。比如設(shè)置鎖,解鎖,移除一塊共享內(nèi)存。
簡(jiǎn)單介紹了API,還要說(shuō)一下一些注意的東西
1.共享內(nèi)存不會(huì)把數(shù)據(jù)寫入磁盤文件中,這個(gè)區(qū)別于mmap
2.即使沒有進(jìn)程綁定在共享內(nèi)存,共享內(nèi)存也不會(huì)消失的。必須通過shmctl或者ipcrm刪除(或者更暴力的方式關(guān)掉電腦)
另外我們可能會(huì)考慮,系統(tǒng)最多創(chuàng)建多少個(gè)共享內(nèi)存,一個(gè)進(jìn)程最多可以綁定多少個(gè)內(nèi)存,一個(gè)共享內(nèi)存創(chuàng)建的size最大最小值是多少。其實(shí)這些設(shè)置在/proc/sys/kernel下面,我們也可以自己寫程序來(lái)讀取。貼一段代碼用來(lái)獲取上面的信息
#define MAX_SHMIDS 8196
int main(int argc,char *argv[])
{
int i,j;
int shmid[MAX_SHMIDS] = {0};
void *addr[MAX_SHMIDS] = {0};
//測(cè)試可以創(chuàng)建多少個(gè)共享內(nèi)存
for ( i = 0;i < MAX_SHMIDS;++i )
{
shmid[i] = shmget(IPC_PRIVATE,1024,0666|IPC_CREAT);
if ( shmid[i] == -1 )
{
printf("create shared memory failed,max create num[%d],%s\r\n",i,strerror(errno));
break;
}
}
for ( int j = 0;j < i;++j )
{
shmctl(shmid[j],IPC_RMID,NULL);
}
//測(cè)試每個(gè)進(jìn)程可以attach的最大數(shù)
for ( i = 0;i < MAX_SHMIDS;++i )
{
shmid[i] = shmget(IPC_PRIVATE,1024,0666|IPC_CREAT);
if ( shmid[i] != -1 )
{
addr[i] = shmat(shmid[i],0,0);
if ( addr[i] == (void *)-1 )
{
printf("process attach shared memory failed,max num[%d],%s\r\n",i,strerror(errno));
shmctl(shmid[i],IPC_RMID,NULL);
break;
}
}
else
{
printf("max num of process attach shared memory is[%d]\r\n",i-1);
break;
}
}
for ( j = 0;j < i;++j )
{
shmdt(addr[j]);
shmctl(shmid[j],IPC_RMID,NULL);
}
//測(cè)試一個(gè)共享內(nèi)存創(chuàng)建最小的size
size_t size = 0;
for ( ;;size++ )
{
shmid[0] = shmget(IPC_PRIVATE,size,0666|IPC_CREAT);
if ( shmid[0] != -1 )
{
printf("create shared memory succeed,min size[%d]\r\n",size);
shmctl(shmid[0],IPC_RMID,NULL);
break;
}
}
//測(cè)試共享內(nèi)存創(chuàng)建最大的size
for ( size = 65536;;size += 1024 )
{
shmid[0] = shmget(IPC_PRIVATE,size,0666|IPC_CREAT);
if ( shmid[0] == -1 )
{
printf("create shared memory failed,max size[%ld],%s\r\n",size,strerror(errno));
break;
}
shmctl(shmid[0],IPC_RMID,NULL);
}
exit(0);
}
好了,下篇開始介紹如何控制讀寫。
posted @
2012-09-06 19:26 梨樹陽(yáng)光 閱讀(2233) |
評(píng)論 (0) |
編輯 收藏
1.拷貝構(gòu)造函數(shù)的形式
對(duì)于類X,如果它的函數(shù)形式如下
a) X&
b) const X&
c) volatile X&
d) const volatile X&
且沒有其他參數(shù)或其他參數(shù)都有默認(rèn)值,那么這個(gè)函數(shù)是拷貝構(gòu)造函數(shù)
X::X(const X&);是拷貝構(gòu)造函數(shù)
X::X(const X&,int val = 10);是拷貝構(gòu)造函數(shù)
2.一個(gè)類中可以存在超過一個(gè)拷貝構(gòu)造函數(shù)
class X {
public:
X(const X&);
X(X&); // OK
};
編譯器根據(jù)實(shí)際情況調(diào)用const拷貝構(gòu)造函數(shù)或非const的拷貝構(gòu)造函數(shù)
3.默認(rèn)的拷貝構(gòu)造函數(shù)行為
a)先調(diào)用父類的拷貝構(gòu)造函數(shù)
b)如果數(shù)據(jù)成員為一個(gè)類的實(shí)例,則調(diào)用該類的拷貝構(gòu)造函數(shù)
c)其他成員按位拷貝
4.默認(rèn)的賦值構(gòu)造函數(shù)行為
a)先調(diào)用父類的賦值構(gòu)造函數(shù)
b)如果數(shù)據(jù)成員為一個(gè)類的實(shí)例,則調(diào)用該類的賦值構(gòu)造函數(shù)
c)其他成員按位拷貝
5.提供顯示的拷貝和賦值構(gòu)造函數(shù)
基本的原則是子類一定要調(diào)用父類的相應(yīng)函數(shù),參考方式
Derive(const Derive& obj):Base(obj)
{
…...
}
Derive& operator =(const Derive &obj)
{
if ( this == &obj )
return *this;
//方式一
Base::operator =(obj);
//方式二
static_cast<Base&>(*this) = obj;
return *this;
}
另外當(dāng)你的成員變量有const或者引用,系統(tǒng)無(wú)法為你提供默認(rèn)的拷貝和賦值構(gòu)造函數(shù),我們必須自己處理這些特殊的情況
posted @
2012-08-31 17:13 梨樹陽(yáng)光 閱讀(1724) |
評(píng)論 (0) |
編輯 收藏
我們?cè)陬惱锩鏁?huì)定義成員函數(shù),同時(shí)我們也希望定義成員函數(shù)指針。因此需要解決3個(gè)問題,第一怎么定義類的函數(shù)指針,第二賦值,第三使用。
我定義一個(gè)類,
class A
{
public:
int add(int,int);
int mul(int,int);
int div(int,int);
};
int A::add(int a,int b)
{
return a + b;
}
int A::mul(int a,int b)
{
return a * b;
}
int A::div(int a,int b)
{
return (b !=0 ? a/b : a);
}
我現(xiàn)在想要定義一個(gè)指針,指向A類型中返回值為int,參數(shù)為兩個(gè)int的函數(shù)指針。熟悉C的同學(xué)馬上會(huì)寫出typedef int (*oper)(int,int)。但是這個(gè)用到C++里就有問題了,
因?yàn)槲覀冎溃惖姆莝tatic成員方法實(shí)際上還有this指針的參數(shù)。比如add方法,它實(shí)際的定義可能會(huì)這樣int add(A* this,int a,int b);因此,我們不能簡(jiǎn)單把C語(yǔ)言里的那套東西搬過來(lái),我們需要重新定義這種類型的指針。正確做法是加上類型,typedef int (A::*Action)(int,int)
typedef int (A::* Action)(int,int);
int main()
{
A a;
Action p = &A::add;
cout << (a.*p)(1,2) << endl;
return 0;
}
Action p = &A::add;這個(gè)就是賦值語(yǔ)句
調(diào)用的時(shí)候注意,直接這樣(*p)(1,2)是不行的,它必須綁定到一個(gè)對(duì)象上(a.*p)(1,2);我們也可以把類的函數(shù)指針定義在類的聲明里。
class A
{
public:
int add(int,int);
int mul(int,int);
int div(int,int);
int (A::*oper)(int,int);
};
上面這種方式是針對(duì)非static成員函數(shù)的,那么static成員函數(shù)的函數(shù)指針應(yīng)該怎么定義呢,還能用上面這種方式嗎?我們知道static成員函數(shù)是沒有this指針的,所以不會(huì)加上的類的限定,它的函數(shù)指針定義方式和C里面的函數(shù)指針定義方式一樣的。
class A
{
public:
int add(int,int);
int mul(int,int);
int div(int,int);
static int sub(int,int);
};
int A::sub(int a,int b)
{
return a - b;
}
int main()
{
int (*pp)(int,int) = &A::sub;
cout << (*pp)(10,5) << endl;
return 0;
}
總結(jié)起來(lái),非static成員函數(shù)的函數(shù)指針需要加上類名,而static的成員函數(shù)的函數(shù)指針和普通的函數(shù)指針一樣,沒有什么區(qū)別。
另外注意一點(diǎn)的是
如果函數(shù)指針定義在類中,調(diào)用的時(shí)候有點(diǎn)區(qū)別。
class A
{
public:
int add(int,int);
int mul(int,int);
int div(int,int);
int (A::*pfunc)(int,int);
};
int main()
{
A a;
a.pfunc = &A::add;
cout << (a.*(a.pfunc))(1,2) << endl;
return 0;
}
posted @
2012-08-14 17:17 梨樹陽(yáng)光 閱讀(1412) |
評(píng)論 (0) |
編輯 收藏