# iOS 一窺并發(fā)編程底層(一)

語歌博客

邏輯控制流

在我們系統(tǒng)中通常是會(huì)有其它程序在運(yùn)行探橱,進(jìn)程是可以告訴每一個(gè)程序它是獨(dú)自在使用處理器。這個(gè)時(shí)候如果有調(diào)試器單步去執(zhí)行程序,就會(huì)出現(xiàn)一系列的程序計(jì)數(shù)器( PC ) 值镜廉,這些值唯一的對(duì)應(yīng)于包含在程序的可執(zhí)行目標(biāo)文件的指令。這個(gè)所謂的 PC 值叫做 邏輯控制流

一句話簡(jiǎn)單的介紹什么是并發(fā):

  • 如果邏輯控制流在時(shí)間上重疊就是并發(fā) (concurrent)

e.g:

  •   往宏觀上講:在計(jì)算機(jī)系統(tǒng)中硬件異常處理程序, 我們進(jìn)行 `Command + C `的時(shí)候
    
  •   往微觀上講:I/O 多路復(fù)用愚战,應(yīng)用程序在一個(gè)進(jìn)程的上下文中顯示地調(diào)度它們的邏輯流娇唯。邏輯流被模型化為狀態(tài)機(jī),數(shù)據(jù)到達(dá)文件描述符后寂玲,主程序顯式地從一個(gè)狀態(tài)轉(zhuǎn)換到另一個(gè)狀態(tài)塔插。
    
  • .... 如果在底層上面扣太多,就不是 iOS 方面的內(nèi)容了敢茁。

我們知道對(duì) 應(yīng)用層的開發(fā) 都是通過對(duì)底層的一個(gè) API 的封裝,這里也會(huì)簡(jiǎn)單介紹一下底層方面的理論。

如果要了解并發(fā)編程就免不了 進(jìn)程,線程 這些字眼留美。


進(jìn)程

我相信從事 IT 行業(yè)的開發(fā)人員彰檬,對(duì)于它是不陌生的伸刃,千篇一律的話我就不多說了。下面就簡(jiǎn)單聊點(diǎn)不常知道的逢倍。
首先進(jìn)程有獨(dú)立的虛擬地址空間捧颅,如果想要和其他流進(jìn)行通信,就是進(jìn)程與進(jìn)程之間進(jìn)行通信较雕,控制流必須使用某種顯示的進(jìn)程間通信機(jī)制 (IPC)

線程

線程是運(yùn)行在一個(gè)單一進(jìn)程上下文的邏輯流,由內(nèi)核進(jìn)行調(diào)度碉哑。

Obj中國(guó) 上我看到有用 pthread 進(jìn)行示例證明。 我這里會(huì)適當(dāng)?shù)难a(bǔ)充一點(diǎn)亮蒋。

Posix 線程(Pthread) 是在 C 程序中處理線程的一個(gè)標(biāo)準(zhǔn)接口扣典,在所有的 Linux 上都適用。 那么 Objective- C 對(duì)它的依賴就可想而知了慎玖。

Pthread 定義了大約 60多個(gè) 個(gè)函數(shù)贮尖,它們分別用來進(jìn)行創(chuàng)建,殺死,和回收線程.對(duì)線程安全地共享數(shù)據(jù),通知對(duì)等線程系統(tǒng)狀態(tài)的變化等等趁怔。

直接適用 Pthread 的函數(shù)是非常繁瑣的,那么到了 OC 這里就免不了對(duì)它進(jìn)行了二次封裝, 就這樣來到了 Cocoa 那么到了 Swift 里面也基本是換湯不換藥的拿來即用湿硝。


現(xiàn)在正式來到多線程的世界

先說點(diǎn)不厭其煩的廢話知識(shí):

在 Objective-C 中常用的加鎖方式有用 @synchronized 來修飾變量,以此來保證變量在作用范圍內(nèi)不會(huì)被其他線程改變润努。

那在 Swift 中是用 objc_sync_enterobjc_sync_exit 配合來使用加鎖

上面提到鎖相關(guān)的一些信息关斜,那么它存在的目的無非就是一個(gè):就是在多線程共享相同的程序變量

那么它在底層的原理是什么?正是這里要講的铺浇。

問題
1.多線程存在的時(shí)候其基礎(chǔ)的內(nèi)存模型是什么痢畜?
2.變量如何映射到內(nèi)存里面去?
3.引用這些變量的線程有多少随抠?

每個(gè)線程都有它自己獨(dú)立的線程上下文,包括線程的 ID, 棧 , 棧指針 ,程序計(jì)數(shù)器, 條件碼, 通用目的寄存器

每個(gè)線程和其他線程一起共享進(jìn)程的上下文的剩余部分裁着。這包括整個(gè)用戶虛擬地址空間,它是由只讀文本,讀/寫數(shù)據(jù), 棧以及所有的共享庫代碼和數(shù)據(jù)區(qū)域組成拱她。

任何線程都可以訪問共享虛擬內(nèi)存的任意位置, 如果眾多線程中的某一個(gè)線程修改了一個(gè)內(nèi)存位置二驰,那其他的線程都能在它讀到這個(gè)位置時(shí)發(fā)現(xiàn)這個(gè)變化。

虛擬內(nèi)存對(duì)相關(guān)變量的一些操作

  1. 全局變量:
    虛擬內(nèi)存的讀/寫區(qū)域只包含每個(gè)全局變量的一個(gè)實(shí)例秉沼,任何線程都可以調(diào)用

  2. 本地變量:
    每個(gè)線程的棧都包含它自己的所有本地自動(dòng)變量

  3. 本地的靜態(tài)變量:本地帶 Static 屬性, 虛擬內(nèi)存的讀/寫區(qū)域只包含程序中聲明的每個(gè)本地靜態(tài)變量的一個(gè)實(shí)例

信號(hào)量

計(jì)數(shù)器 (引用計(jì)數(shù))
同步錯(cuò)誤 synchronization error
進(jìn)度圖 progress graph


計(jì)數(shù)器 (引用計(jì)數(shù)) 與 同步錯(cuò)誤 (synchronization error)

在多線程訪問同一個(gè)全局變量的時(shí)候桶雀,我們查看對(duì)計(jì)數(shù)相關(guān)的匯編代碼,過程大致如下:

  1. 加載全局變量 cnt 到累加寄存器 %rdx (當(dāng)前線程的寄存器 %rdx 的值)
  2. 增加 %rdx 的指令
  3. %rdx的更新值存回到共享變量 cnt 的指令

當(dāng)然在iOS當(dāng)中不會(huì)直接這樣計(jì)數(shù)唬复,因?yàn)檫@樣會(huì)存在一個(gè)很大的問題:

當(dāng)兩個(gè)線程同時(shí)對(duì)一個(gè)計(jì)數(shù)器的值進(jìn)行讀取矗积,并加1,再將結(jié)果寫到內(nèi)存中去敞咧,這個(gè)時(shí)候計(jì)數(shù)器就會(huì)出現(xiàn)問題棘捣。因?yàn)橛?jì)數(shù)器加了兩次而寫到內(nèi)存中確是相當(dāng)于只加了一次的那個(gè)值

引用 Obj中國(guó) 上面一個(gè)例子:

線程 A 和 B 都從內(nèi)存中讀取出了計(jì)數(shù)器的值,假設(shè)為 1 休建,然后線程A將計(jì)數(shù)器的值加1乍恐,并將結(jié)果 2 寫回到內(nèi)存中评疗。同時(shí),線程B也將計(jì)數(shù)器的值加 1 茵烈,并將結(jié)果 2 寫回到內(nèi)存中百匆。實(shí)際上,此時(shí)計(jì)數(shù)器的值已經(jīng)被破壞掉了呜投,因?yàn)橛?jì)數(shù)器的值 1 被加 1 了兩次加匈,而它的值卻是 2。

在iOS 開發(fā)的應(yīng)用層上面來看就是加鎖等等一系列的操作仑荐。在真正核心底層方面好多文章是沒有具體去講的雕拼,您可以綜合性的看看其他的文章,推薦 Obj中國(guó) 上面關(guān)于多線程系統(tǒng)的講法释漆。好了悲没,我們繼續(xù):

進(jìn)度圖 progress graph

將 n 個(gè)并發(fā)線程的執(zhí)行抽象為一條 n 維 笛卡爾空間 中的軌跡線.

每條軸的 k 對(duì)應(yīng)線程 k 的進(jìn)度。每個(gè)點(diǎn)代表線程 k已經(jīng)完成了指令 I_k的狀態(tài)男图。

我們上面講的:

在多線程訪問同一個(gè)全局變量的時(shí)候示姿,我們查看對(duì)計(jì)數(shù)相關(guān)的匯編代碼,過程大致分為3步

我們這里設(shè)定在 A 線程的時(shí)候步驟為:

第一步 第二步 第三步
A1 A2 A3

同理設(shè)定在 B 線程的時(shí)候步驟為:

第一步 第二步 第三步
B1 B2 B3

這個(gè)時(shí)候我們來看下圖

我們看到圖中有一個(gè)點(diǎn) (A1,B3).
這個(gè)點(diǎn)的意思就是:當(dāng)線程 A 完了第 A1 狀態(tài)的同時(shí)逊笆,線程 B 完成了 B3 狀態(tài)栈戳。

使用進(jìn)度圖的目的就是講指令執(zhí)行模型轉(zhuǎn)化為從一種狀態(tài)到另一種狀態(tài)的轉(zhuǎn)換。

這樣就可以把程序的執(zhí)行歷史轉(zhuǎn)換為狀態(tài)空間中的一條軌跡線难裆。

對(duì)于線程不管是 A 或者 B 也好子檀,對(duì)全局變量的的操作(A1,A2,A3)步驟或者 (B1,B2,B3)步驟的過程中構(gòu)成了一個(gè)臨界區(qū),這個(gè)臨界區(qū)不應(yīng)該和其他進(jìn)程的臨界區(qū)交替執(zhí)行乃戈。我們確保每個(gè)線程在執(zhí)行它的臨界區(qū)中的指令時(shí)褂痰,擁有對(duì)共享變量 的 互斥 的訪問( Mutually exclusive access). 通常這種現(xiàn)象稱為互斥(Mutual exclusion).

這樣在上圖里面會(huì)出現(xiàn)這樣的規(guī)則:相同指令不能再同一時(shí)刻完成,對(duì)角線的線是不存在的症虑。


兩個(gè)臨界區(qū)的交集形成的狀態(tài)空間區(qū)域稱為不安全區(qū)(unsafe region)

安全軌跡線:不在不安全區(qū)的軌跡線
不安全軌跡線:雷區(qū)的軌跡線

任何安全軌跡線都將正確地更新共享計(jì)數(shù)器缩歪。為了保證任意的全局變量在并發(fā)線程的正確執(zhí)行,我們就必須以某種方式同步線程谍憔,使他們總是有一條安全軌跡線匪蝙。其思想原理的基本思想就是基于 信號(hào)量

信號(hào)量: (semaphore) 一種特殊類型的變量。
信號(hào)量以s表示.是具有非負(fù)整數(shù)值的全局變量习贫,只能有兩種特殊的操作來處理逛球,這兩種操作稱為 P 和 V:

P(s): 如果 s 是非零的,那么P 將 s 減1苫昌,并且立即返回颤绕。如果 S 為零,那么就掛起這個(gè)線程,直到 s 為零奥务,而一個(gè) V 操作會(huì)重啟這個(gè)線程涕烧。在重啟之后,P 操作將 s減1,并將控制返回給調(diào)用者汗洒。

V(s): V操作將 s 加1。如果有任何線程阻塞在 P 操作等待 s 變成非零,那么 V 操作會(huì)重啟這些線程中的一個(gè)父款,然后該線程將 s 減1溢谤,完成它的 P 操作。

P 中的測(cè)試和減1操作是不可分割的憨攒,一旦預(yù)測(cè)信號(hào)量s 變?yōu)榉橇闶郎保蜁?huì)將s減1,不能有中斷操作肝集,這個(gè)過程中不會(huì)有中斷瞻坝。 V 的加1 操作也是不可分割的。

沒有中斷的操作

加載 加1 存儲(chǔ)信號(hào)

ps: V 的定義中沒有定義等待線程被重啟動(dòng)的順序杏瞻。唯一的要求是 V 必須只能重啟一個(gè)正在等待的線程所刀。因此,當(dāng)有多個(gè)線程在等待同一個(gè)信號(hào)量時(shí)捞挥,就不能預(yù)測(cè) V 操作要重啟哪一個(gè)線程浮创。

P和V 的定義確保了一個(gè)正在運(yùn)行的程序絕不可能進(jìn)入這一種狀態(tài),也就是一個(gè)正確初始化了的信號(hào)量有一個(gè)負(fù)值砌函。這個(gè)屬性稱為 信號(hào)量不變性(semaphore invariant)

使用信號(hào)量來實(shí)現(xiàn)互斥

作用是:
將每個(gè)**全局變量 **與 一個(gè)信號(hào)量 s = 1 聯(lián)系起來斩披,然后用 P(s) 和 V(s) 操作將相應(yīng)的臨界區(qū)包圍起來。這種方式成為 二元信號(hào)量 (binary semaphore),它的值要么是 0 要么是 1讹俊。以提供互斥為目的的二元型號(hào)量常常稱為 互斥鎖 (mutex).

那么在一個(gè)互斥鎖上執(zhí)行 P 操作稱為對(duì)互斥鎖加鎖垦沉。執(zhí)行 V操作稱為對(duì)互斥鎖解鎖。對(duì)一個(gè)互斥鎖加了鎖但是還沒有解鎖的線程稱為占用了這個(gè)互斥鎖仍劈。 一個(gè)被用作一組可用資源的計(jì)數(shù)器的信號(hào)量被稱為 計(jì)數(shù)信號(hào)量厕倍。

如上面的雷區(qū)圖,在雷區(qū)內(nèi)因?yàn)樾盘?hào)量的不確定性故: s < 0

以上看到的仍然是坑: 因?yàn)樯厦媸菃翁幚砥鞯闹v解
但是有一個(gè)是萬用的:同步對(duì)共享變量的訪問是必須的耳奕。

多線程中對(duì)相同資源的訪問:
案例1:
在多媒體開發(fā)過程中對(duì)視頻的幀編碼绑青,并實(shí)時(shí)播放。這個(gè)時(shí)候就會(huì)有一個(gè)緩存的東西存在屋群,其存在的目的是為了減少視頻流的抖動(dòng)闸婴,引起的原因是幀的編碼與解碼時(shí)與數(shù)據(jù)相關(guān)的差異引起的。

案例2:
我們開發(fā)過程中對(duì)手機(jī)屏幕點(diǎn)擊事件的產(chǎn)生后芍躏,該事件先進(jìn)入緩存中邪乍,然后多線程根據(jù)優(yōu)先級(jí)來從緩沖區(qū)里面取出該事件進(jìn)行響應(yīng)。這就能很好解釋有時(shí)點(diǎn)擊屏幕卡屏了一會(huì)兒才響應(yīng)。

饑餓問題:
這個(gè)網(wǎng)上帖子泛濫: 傳送門 Obj中國(guó)

多個(gè)線程并行處理分配給它們的區(qū)域處理方法:
主線程給其他開的線程一個(gè)整數(shù)理解為該線程的 ID庇楞。每個(gè)線程用它的ID來決定它應(yīng)該計(jì)算序列的哪一部分榜配。

并行程序的性能

運(yùn)行時(shí)間是衡量程序性能的最終標(biāo)準(zhǔn)。相對(duì)衡量標(biāo)準(zhǔn)能夠說明并行程序有多好地利用了潛在的并行性。
并行程序的加速比(speedup)通常定義為: Sp = T1/Tp

p 是處理器的核樹畴博,Tk 是在 K 個(gè)核上的運(yùn)行時(shí)間可很。這個(gè)公式被稱為:強(qiáng)擴(kuò)展(strong scaling).

  1. 當(dāng)T1是程序順序執(zhí)行版本的執(zhí)行時(shí)間時(shí),Sp稱為 絕對(duì)加速比 (absolute speedup).
  2. 當(dāng)T1是程序并行版本在一個(gè)核上的執(zhí)行時(shí)間烙心,Sp稱為 相對(duì)加速比 (absolute speedup).

絕對(duì)加速比會(huì)比相對(duì)加速比更加難以測(cè)量,因?yàn)闇y(cè)量絕對(duì)加速比需要程序的兩種不同的版本乏沸。對(duì)于復(fù)雜的并行代碼淫茵,創(chuàng)造一個(gè)獨(dú)立的順序版本也不現(xiàn)實(shí)。

效率: Ep = Sp/p = T1/pTp

弱擴(kuò)展:(weak scaling): 在增加處理器數(shù)量的同時(shí)蹬跃,增加問題的規(guī)模匙瘪,這樣隨著處理器的數(shù)量的增加,每個(gè)處理器執(zhí)行的工作量保存不變蝶缀,在這樣的情況下加速比和效率被表達(dá)為單位時(shí)間完成的工作量丹喻。

線程安全

首先被稱為線程安全是當(dāng)且僅當(dāng)被多個(gè)多線程反復(fù)的調(diào)用,它才會(huì)一直產(chǎn)生正確的結(jié)果翁都。如果一個(gè)函數(shù)設(shè)計(jì)的不是線程安全的驻啤,它就是線程不安全的。

線程不安全的函數(shù)定義:

  1. 對(duì)全局變量的保護(hù)
  2. 保存跨越多個(gè)調(diào)用的狀態(tài)函數(shù)荐吵。如:隨機(jī)數(shù)生產(chǎn)的函數(shù)
  3. 返回指向靜態(tài)變量的指針的函數(shù)骑冗。
  4. 調(diào)用線程不安全函數(shù)的函數(shù)。

每個(gè)的具體例子有點(diǎn)多分下一章節(jié)進(jìn)行

iOS 一窺并發(fā)編程底層(二)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末先煎,一起剝皮案震驚了整個(gè)濱河市贼涩,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌薯蝎,老刑警劉巖遥倦,帶你破解...
    沈念sama閱讀 222,000評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異占锯,居然都是意外死亡袒哥,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,745評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門消略,熙熙樓的掌柜王于貴愁眉苦臉地迎上來堡称,“玉大人,你說我怎么就攤上這事艺演∪唇簦” “怎么了桐臊?”我有些...
    開封第一講書人閱讀 168,561評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)晓殊。 經(jīng)常有香客問我断凶,道長(zhǎng),這世上最難降的妖魔是什么巫俺? 我笑而不...
    開封第一講書人閱讀 59,782評(píng)論 1 298
  • 正文 為了忘掉前任认烁,我火速辦了婚禮,結(jié)果婚禮上介汹,老公的妹妹穿的比我還像新娘砚著。我一直安慰自己,他們只是感情好痴昧,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,798評(píng)論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著冠王,像睡著了一般赶撰。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上柱彻,一...
    開封第一講書人閱讀 52,394評(píng)論 1 310
  • 那天豪娜,我揣著相機(jī)與錄音,去河邊找鬼哟楷。 笑死瘤载,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的卖擅。 我是一名探鬼主播鸣奔,決...
    沈念sama閱讀 40,952評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼惩阶!你這毒婦竟也來了挎狸?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,852評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤断楷,失蹤者是張志新(化名)和其女友劉穎锨匆,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體冬筒,經(jīng)...
    沈念sama閱讀 46,409評(píng)論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡恐锣,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,483評(píng)論 3 341
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了舞痰。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片土榴。...
    茶點(diǎn)故事閱讀 40,615評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖响牛,靈堂內(nèi)的尸體忽然破棺而出鞭衩,到底是詐尸還是另有隱情学搜,我是刑警寧澤,帶...
    沈念sama閱讀 36,303評(píng)論 5 350
  • 正文 年R本政府宣布论衍,位于F島的核電站瑞佩,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏坯台。R本人自食惡果不足惜炬丸,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,979評(píng)論 3 334
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望蜒蕾。 院中可真熱鬧稠炬,春花似錦、人聲如沸咪啡。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,470評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽撤摸。三九已至毅桃,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間准夷,已是汗流浹背钥飞。 一陣腳步聲響...
    開封第一講書人閱讀 33,571評(píng)論 1 272
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留衫嵌,地道東北人读宙。 一個(gè)月前我還...
    沈念sama閱讀 49,041評(píng)論 3 377
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像楔绞,于是被迫代替她去往敵國(guó)和親结闸。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,630評(píng)論 2 359

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

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理酒朵,服務(wù)發(fā)現(xiàn)膀估,斷路器,智...
    卡卡羅2017閱讀 134,704評(píng)論 18 139
  • 從三月份找實(shí)習(xí)到現(xiàn)在耻讽,面了一些公司察纯,掛了不少,但最終還是拿到小米针肥、百度饼记、阿里、京東慰枕、新浪具则、CVTE、樂視家的研發(fā)崗...
    時(shí)芥藍(lán)閱讀 42,277評(píng)論 11 349
  • 引用自多線程編程指南應(yīng)用程序里面多個(gè)線程的存在引發(fā)了多個(gè)執(zhí)行線程安全訪問資源的潛在問題具帮。兩個(gè)線程同時(shí)修改同一資源有...
    Mitchell閱讀 1,998評(píng)論 1 7
  • 前言: 文韜互聯(lián)早前注冊(cè)了百家號(hào),一直沒有時(shí)間發(fā)布匪凡,最近一段時(shí)間開始撿起這個(gè)事情膊畴,將在百家號(hào)上發(fā)布原創(chuàng)文章,專注互...
    林文韜同學(xué)閱讀 196評(píng)論 0 0
  • 伍家的七星伴月被挖后病游,男人們都回來了唇跨,厄運(yùn)才剛剛啟幕,為商的伍一郎衬衬,潮州經(jīng)商被騙买猖,多年積累的財(cái)富片刻消失殆盡...
    土壯閱讀 446評(píng)論 0 0