青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

Fork me on GitHub
隨筆 - 215  文章 - 13  trackbacks - 0
<2016年3月>
282912345
6789101112
13141516171819
20212223242526
272829303112
3456789


專注即時通訊及網游服務端編程
------------------------------------
Openresty 官方模塊
Openresty 標準模塊(Opm)
Openresty 三方模塊
------------------------------------
本博收藏大部分文章為轉載,并在文章開頭給出了原文出處,如有再轉,敬請保留相關信息,這是大家對原創作者勞動成果的自覺尊重??!如為您帶來不便,請于本博下留言,謝謝配合。

常用鏈接

留言簿(1)

隨筆分類

隨筆檔案

相冊

Awesome

Blog

Book

GitHub

Link

搜索

  •  

積分與排名

  • 積分 - 220943
  • 排名 - 117

最新評論

閱讀排行榜

http://www.cnblogs.com/zhengsyao/p/erlang_race_condition_in_digraph.html
雖然 Erlang 的廣告說得非常好,functional、share-nothing、消息傳遞,blah blah 的,好像用 Erlang 寫并發程序就高枕無憂了,但是由于 Erlang 信奉高度實用主義的哲學,所以 Erlang 中有各種各樣的后門,使得 Erlang 是一個不那么 pure 的 functional 語言,而且也是允許 share something 的。

比如說,ETS 就是一個大后門。ETS 本是為實現 Mnesia 數據庫而實現的一個“基于哈希表的無結構化 term 存儲”,但是 ETS 太好用了,能滿足高性能的字典需求,而且還可以全局訪問,完全擺脫 Erlang 設置的 share nothing、immutable variable 之類的 functional 束縛,所以很多程序會喜歡使用 ETS。

Erlang 的(為了避免錯誤而設置的)種種約束在這些使用了這些后門的情況下也會喪失威力。這不,我在標準庫的 digraph 模塊中就發現了一個潛在的 bug。下面先說一下使用 ETS 造成并發 bug 的模式,然后再看 digraph 中的這個潛在 bug。

Erlang 中有很多這樣的代碼:V2 = do_something(V1),如果 V1 和 V2 是同樣類型的復雜數據結構,根據 Erlang 的語義,do_something 對 V1 只是讀操作,然后生成新的字段,最后創建出新的 V2 返回,V1 應該是不動的。這樣其他進程也可以放心地訪問 V1,至少 V1 的狀態在系統中是一致的(consistent)。如果 V1 中使用了 ETS,并且 do_something 對 ETS 做操作了,那么這個函數就產生副作用了,以上的一致性可能就無法保證了,例如假設 V1 是這樣的數據結構:

-record(some_struct, {tab1 =  ets:tab(),
                      tab2 =  ets:tab(),
                      var1 =  integer()}).

這個數據結構中有兩個 ETS 表,說明這個數據結構的狀態是由兩個 ETS 表的狀態決定的。雖然 Erlang 運行時能保證單個 ETS 表的并發訪問,但是多個表的一致性還需要程序自己來維護,因此這個數據結構在多進程訪問的情況下就會出現競爭條件。下面我們看看 digraph 模塊中的潛在 bug。

digraph 模塊是用來表示有向圖(directed graph)的。有向圖就是在一個圖中,要考慮邊(edge)中頂點(vertex)的順序。有向圖的數據結構中要保存頂點的集合以及邊的集合。某個頂點的鄰居(neighbour)指的是和這個頂點相連的頂點,由于有向圖是有方向的,所以一個頂點會有入鄰居(in-neighbour)和出鄰居(out-neighbour),前者指的是有邊射入當前頂點的頂點,后者指的是當前頂點有邊射出到的頂點。好了這是基本概念。下面我們看 digraph 模塊使用的數據結構:

1 -record(digraph, {vtab = notable :: ets:tab(),
2                   etab = notable :: ets:tab(),
3                   ntab = notable :: ets:tab(),
4                   cyclic = true  :: boolean()}).

digraph 記錄體中連續 3 個 ETS 表,看上去就有不好的事情可能發生。vtab 是頂點的表,etab 是邊的表,ntab 維護了鄰居的關系。digraph 模塊對 digraph 數據結構并發訪問做了控制,允許設置兩種模式:protected 和 private。前者只允許 digraph 的所有者進程修改,但是其他進程都可以訪問;后者只允許所有者進程修改和訪問,其他進程不能訪問(其實就是設置了 ETS 表的訪問權限)。

下面創建一個簡單的有向圖,創建幾個節點和邊:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
5> DG = digraph:new().       
{digraph,28692,32789,36886,true}
6> digraph:add_vertex(DG, v1).
v1
省略...
11> digraph:add_edge(DG, v1, v2).
['$e'|0]
省略...
16> digraph:in_neighbours(DG, v1).
[]
17> digraph:in_neighbours(DG, v2).
[v1]
省略...
21> digraph:out_neighbours(DG, v1).
[v5,v4,v3,v2]

命令 5> 創建了新的 digraph,輸出的 DG 值中包含了幾個 ETS id,然后添加頂點和邊。v1 頂點的入鄰居沒有,出鄰居包括 v2、v3、v4 和 v5。下面展示了這幾個 ETS 表中的項:

vertices 表中是頂點,edges 表中是邊,neighbour 表中保存了 in 和 out 的關系。其實 neighbour 表中的內容可以從 edges 表中推導出來,但是這個表在這里可以做一個緩存作用,迅速查出一個節點的鄰居關系,而不用掃描 edges 表。

從這里我們應該可以嗅到 bug 的味道了:edges 表和 neighbour 表有映射關系,如果修改一個表,那么另一個表也需要修改,否則就會出現數據不一致(inconsistent)的情況。我們來看插入邊的代碼:

復制代碼
 1 add_edge(G, V1, V2) ->
 2     do_add_edge({new_edge_id(G), V1, V2, []}, G).
 3 
 4 do_add_edge({E, V1, V2, Label}, G) ->
 5     case ets:member(G#digraph.vtab, V1) of
 6         false -> {error, {bad_vertex, V1}};
 7         true  ->
 8             case ets:member(G#digraph.vtab, V2) of
 9                 false -> {error, {bad_vertex, V2}};
10                 true ->
11                     case other_edge_exists(G, E, V1, V2) of
12                         true -> {error, {bad_edge, [V1, V2]}};
13                         false when G#digraph.cyclic =:= false ->
14                             acyclic_add_edge(E, V1, V2, Label, G);
15                         false ->
16                             do_insert_edge(E, V1, V2, Label, G)
17                     end
18             end
19     end.
20 
21 do_insert_edge(E, V1, V2, Label, #digraph{ntab=NT, etab=ET}) ->
22     ets:insert(NT, [{{out, V1}, E}, {{in, V2}, E}]),
23     ets:insert(ET, {E, V1, V2, Label}),
24     E.
復制代碼

diagraph:add_edge/3 是發布的接口,do_add_edge/2 做一些頂點是否存在的判斷,檢查是否能添加邊,最后 do_insert_edge/5 真正負責把邊插入相應的 ETS 表??梢钥闯觯?22 行插入鄰居表,第 23 行插入邊表。

這里就可能出現問題了:根據 Erlang 調度器的規則,第 22 行執行完成之后,由于 ets:insert/2 是一個 BIF 調用,因此進程有可能會被搶占。如果此時進程被搶占,那么 digraph 就處于一個 inconsistent 狀態了。如下圖所示:

假設這個 digraph 是 protected,那么其他進程是可以訪問的。如果其他進程需要 joint 訪問這兩個表的話,有可能就會出問題。果然,訪問鄰居的接口 digraph:in_neighbours/2 就是這樣一個函數,下面來看代碼:

復制代碼
 1 in_neighbours(G, V) ->
 2     ET = G#digraph.etab,
 3     NT = G#digraph.ntab,
 4     collect_elems(ets:lookup(NT, {in, V}), ET, 2).
 5 
 6 collect_elems(Keys, Table, Index) ->
 7     collect_elems(Keys, Table, Index, []).
 8 
 9 collect_elems([{_,Key}|Keys], Table, Index, Acc) ->
10     collect_elems(Keys, Table, Index,
11           [ets:lookup_element(Table, Key, Index)|Acc]);
12 collect_elems([], _, _, Acc) -> Acc.
復制代碼

in_neighbours/2 先提取出兩個表 ET 和 NT,分別是邊表和鄰居表,然后調用 collect_elems/3,相當于做一個 joint 查詢。第 4 行首先查詢鄰居表 ets:lookup(NT, {in, V}),也就是在鄰居表中查詢節點 V 入邊。查到邊之后,collect_elems/4 的第 11 行再從邊表中去查這條邊的頂點。那么如果出現上圖中的情況,在添加邊的時候在鄰居表中插入完成之后進程被切出,那么 edges 表中的第 4 行還不存在,而 neighbours 表中圈出的那兩行是存在的。那么 collect_elems/4 執行到 11 行的 ets:lookup_element 就會拋出異常。

以上就是在標準庫的 digraph 模塊中發生競爭條件的一個例子。對于這個具體的問題來說,把 do_insert_edge/5 中那兩行 insert 換一下位置就好了,先插入邊表,再插入鄰居表,畢竟后面的表可以由前面的表推導出來,所以在訪問的時候先訪問后面的表在訪問前面的表不會出現查不到的情況,盡管本質上看數據還是有可能不一致。所以雖然可以修掉這個 bug,但是這種風格還是有風險的,比如說有 3 個或更多的表需要一致的情況。其實 digraph 模塊這種風格:創建一個數據結構,得到一個句柄,然后大家都可以修改和訪問的風格不是非常“Erlangic”。應該是像這樣的設計:DG = digraph:new(), DG1 = digraph:add_vertex(DG, v1)... 。也就是說,每次修改之后就產生一個新的變量。當然,這樣在內部就不能用 ETS 來實現了,因為每次修改的是 ETS 表,而 DG 變量綁定的 #digraph{} 里面的表 id 字段的值又沒變,所以 DG 和 DG1 的值實際還是一樣的,只是表示的意義不同了,可怕的副作用啊。

至于這是不是 bug,可能有人會爭論了:至少 ETS 查詢會拋 badarg 啊,然后這個異常會傳播到 in_neighours/2 的調用者啊,所以在調用的時候要捕捉異常啊??墒?digraph 文檔又沒說會拋異常,看源碼的時候,也沒有語法結構注明一個函數會拋什么異常,那我怎么知道什么時候要捉異常呢?就算我養成調什么函數都捉異常的習慣,可是我捉到了異常又怎么知道是怎么回事呢?特別是這種并發相關的問題,下次復現的幾率又像中獎一樣。所以 Erlang 這門語言真是讓人又愛又恨啊。

不過不爭論語言本身了,言歸正傳,本文的結論就是:在使用 ETS 的時候要注意 ETS 是全局共享的內存,要注意數據一致性的問題。



erlang 參數傳遞時是賦值or引用?
請教一個基礎問題,erlang 參數傳遞時是賦值or引用?
找不到方法測試。
求教了。

答:
erlang 中怎么會有引用這種說法,變量都是拷貝過去的。

問:
無法實現引用,是么?
我在寫的東西要傳一個大狀態。
所以疑惑了一下。

答:
erlang 所有的變量都只能在函數中傳遞,沒有全局變量這一說。
你傳遞狀態是通過改變函數參數實現的


問:
我不是很清楚當參數很大時,進行拷貝的效率如何.
能給我解惑一下么?


越大效率越低
另外是原子atom ,erlang 的垃圾回收機制是不回收的,除非運行結束。

問:
試想gen_server中call(_From,Status,Args)被調用時,Status就被改變了。這是不是意味著這里的Status是被引用傳遞進去的呢
我有闡述清楚么?


答:
不會
Status 
的值
不會被函數改變
函數得到的只是一個新的參數邦定
綁定

簡單的說
erlang 的參數都是拷貝方式的


廣州-手  14:54:35
對,這就是內存共享。

返源
不見得多少,你的每一個函數結束就會回收當中的內存。
不存在引用

廣州-手   14:53:52
沒有引用的參數傳遞 不知會吃掉我程序的性能

上海-PHP-返源   14:54:34
如果有引用就有副作用
就沒辦法獨自運行

廣州-手   14:54:10
不知會吃掉多少性能。
但是我的status是一條很長的記錄。

廣州-手 )  14:55:23
稍稍有點擔憂。暫時還不能測試。
廣州-手   14:56:23
你剛說的參數 愈大效率越低,是怎么回事?
上海-PHP-返源   14:57:02
記錄是單獨存儲的
不過你傳遞的時候還是拷貝

廣州-手   14:56:47
恩。


上海-PHP-返源    14:57:40
但這里具體內部實現有很多方案
譬如你不寫,只是取,我就可以共用內存。

廣州-手    14:58:03
怎么實現,求指導。

上海-PHP-返源    14:58:56
這個跟你實現沒關系哈,我說的erlang 內部。
總之你減少參數的大小
有利提高速度

廣州-手   14:58:57
這個意思是erlang的自動優化么?

上海-PHP-返源    14:59:40

是的

本文轉自Erlang中國(www.erlangchina.NET
原文網址:http://www.erlangchina.Net/thread-845-1-1.html


 

posted on 2017-01-15 17:17 思月行云 閱讀(313) 評論(0)  編輯 收藏 引用 所屬分類: Erlang
青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            亚洲影院在线| 亚洲国产福利在线| 国产亚洲精品久久久久久| 国产精品久久久久影院色老大 | 久久中文字幕一区| 欧美大片在线观看一区| 欧美黄色小视频| 国产精品白丝av嫩草影院| 国产九九视频一区二区三区| 国产一区二区三区网站| 在线观看欧美成人| 亚洲精一区二区三区| 亚洲一区二区毛片| 欧美在线免费视屏| 亚洲国产成人av好男人在线观看| 99精品国产一区二区青青牛奶| 亚洲欧美成人| 欧美成人a视频| 国产精品欧美日韩一区二区| 国内外成人在线视频| 99国产精品99久久久久久| 性欧美超级视频| 欧美激情在线狂野欧美精品| 这里只有精品视频| 久久这里有精品视频| 欧美午夜久久久| 亚洲高清中文字幕| 久久成人精品| 亚洲欧洲日本专区| 久久久精品一品道一区| 欧美日韩综合另类| 亚洲国产老妈| 久久精品在线播放| av不卡免费看| 美日韩在线观看| 韩国av一区二区| 亚洲天天影视| 亚洲福利视频二区| 欧美一区二区视频97| 欧美亚男人的天堂| 亚洲第一视频| 久久综合伊人77777麻豆| 亚洲一区二区在线视频| 久久久久久久综合| 欧美视频中文字幕| 亚洲国产视频一区| 久久精品国亚洲| 中文欧美日韩| 欧美日韩综合一区| 一区二区三区国产精品| 亚洲国产精品电影| 久热国产精品视频| 在线精品亚洲一区二区| 老司机午夜精品| 久久久亚洲高清| 国内综合精品午夜久久资源| 久久久久高清| 久久狠狠亚洲综合| 国产在线观看91精品一区| 欧美亚洲自偷自偷| 亚洲午夜一区二区| 国产乱码精品一区二区三区av| 亚洲亚洲精品三区日韩精品在线视频| 欧美激情一区二区久久久| 欧美.www| 日韩视频二区| 日韩一级欧洲| 国产精品免费视频观看| 小处雏高清一区二区三区| 一本久久综合| 国产欧美91| 久久亚洲精品一区| 免费的成人av| 亚洲视屏在线播放| 亚洲午夜在线视频| 国产一区二区在线免费观看| 久久另类ts人妖一区二区| 久久手机精品视频| av成人免费观看| 亚洲欧美福利一区二区| 激情校园亚洲| 91久久国产综合久久| 欧美精品一二三| 亚欧美中日韩视频| 老司机午夜精品视频| 日韩视频一区二区| 亚洲欧美日韩第一区| 亚洲成色999久久网站| 亚洲日本久久| 国产欧美一区二区精品忘忧草| 免费欧美网站| 欧美日韩另类一区| 久久天堂成人| 欧美日韩在线第一页| 免费日韩视频| 国产精品美女久久久久久免费 | 国产一区视频观看| 欧美大片在线观看| 国产精品theporn| 久久精品1区| 欧美日韩亚洲天堂| 免播放器亚洲| 亚洲精选久久| 亚洲欧美一区二区激情| 国产在线播放一区二区三区| 亚洲激情视频在线播放| 国产毛片一区二区| 亚洲日本中文字幕免费在线不卡| 国产亚洲午夜| 亚洲网站视频| 夜夜嗨av一区二区三区| 久久免费视频网| 亚洲欧美日韩国产中文| 欧美华人在线视频| 老鸭窝亚洲一区二区三区| 欧美日韩一级视频| 欧美国产视频一区二区| 国产婷婷色一区二区三区四区| 日韩视频在线播放| 亚洲精品久久久蜜桃| 久久久天天操| 久久精品国亚洲| 国产欧美高清| 亚洲主播在线播放| 亚洲手机视频| 欧美激情中文不卡| 欧美国产在线观看| 在线观看视频亚洲| 久久久久久久精| 久久精品成人欧美大片古装| 国产精品美女在线| 亚洲一二三区精品| 亚洲一区中文| 欧美色综合网| 亚洲永久精品国产| 久久爱www久久做| 国产麻豆一精品一av一免费| 一本色道久久88精品综合| 亚洲一区自拍| 国产精品女同互慰在线看| 亚洲午夜女主播在线直播| 午夜久久99| 国内精品久久久久影院色| 久久精品官网| 鲁大师成人一区二区三区 | 中文精品在线| 欧美色123| 亚洲欧美精品suv| 久久九九国产精品| 怡红院精品视频| 免费日韩一区二区| 亚洲免费av电影| 欧美亚洲综合在线| 国内成+人亚洲+欧美+综合在线| 久久久久久综合| 亚洲日本激情| 久久高清国产| 亚洲精品一区二区三区福利| 欧美三区在线视频| 欧美一区中文字幕| 亚洲国产精品视频| 午夜性色一区二区三区免费视频| 国产在线播精品第三| 欧美国产免费| 亚洲欧美视频一区| 欧美电影在线播放| 午夜欧美不卡精品aaaaa| 亚洲国产精品成人精品| 国产精品xvideos88| 久久久欧美精品sm网站| 亚洲丰满少妇videoshd| 亚洲一区欧美一区| 久久久久高清| 国内欧美视频一区二区| 欧美freesex交免费视频| 日韩视频一区二区三区在线播放| 午夜精品视频| 日韩视频国产视频| 国产综合色精品一区二区三区| 欧美激情亚洲一区| 欧美在线网站| 一区二区三区四区在线| 免费在线观看成人av| 亚洲免费影视| 亚洲黄色在线| 在线观看av一区| 国产日韩精品久久久| 欧美日韩一区免费| 欧美不卡高清| 欧美在线视频一区二区三区| 亚洲精品在线免费| 免费视频一区| 久久久精品国产免大香伊| 亚洲午夜精品视频| 亚洲精品国精品久久99热一| 国内精品国产成人| 国产精品日本欧美一区二区三区| 欧美日韩精品久久久| 免费在线看成人av| 久久久人成影片一区二区三区观看|