進(jìn)程-線程-協(xié)程[2021-04-23]

進(jìn)程

進(jìn)程定義(程序段,相關(guān)數(shù)據(jù)段和PCB構(gòu)成進(jìn)程實(shí)體,又稱為進(jìn)程映像):

  1. 進(jìn)程是程序的一次執(zhí)行
  2. 進(jìn)程是一個程序機(jī)器數(shù)據(jù)在處理機(jī)上順序執(zhí)行時所發(fā)生的活動
  3. 進(jìn)城是程序在一個數(shù)據(jù)集合上運(yùn)行的過程,它是系統(tǒng)進(jìn)行資源分配和調(diào)度的一個獨(dú)立單位。

進(jìn)程的三種主要狀態(tài):

  1. 就緒態(tài)->當(dāng)進(jìn)程分配到除CPU以外所有必要資源后窍帝,只要再獲得CPU,便可立即執(zhí)行诽偷,進(jìn)程這時候的狀態(tài)稱為就緒狀態(tài)坤学。在一個系統(tǒng)中處于就緒狀態(tài)的進(jìn)程可能有多個,通常將它們排成一個隊列报慕,稱為就緒隊列深浮。
  2. 執(zhí)行狀態(tài)->進(jìn)程已獲得CPU,其程序正在執(zhí)行眠冈。在單處理機(jī)系統(tǒng)中飞苇,只有一個進(jìn)程處于執(zhí)行狀態(tài)菌瘫,在多處理機(jī)系統(tǒng)中,則有多個進(jìn)程處于執(zhí)行狀態(tài)布卡。
  3. 阻塞狀態(tài)->正在執(zhí)行的進(jìn)程由于發(fā)生某件事請而暫時無法繼續(xù)執(zhí)行時雨让,便放棄處理機(jī)而處于暫停狀態(tài),亦即進(jìn)程的執(zhí)行受到阻塞忿等,把這種暫停狀態(tài)稱為阻塞狀態(tài)栖忠,有時也稱為等待狀態(tài)狀態(tài)或封鎖狀態(tài)。典型事件:請求I/O贸街,申請緩沖空間等庵寞。

進(jìn)程是一個實(shí)體。每一個進(jìn)程都有它自己的地址空間薛匪,一般情況下捐川,包括文本區(qū)域(text region)、數(shù)據(jù)區(qū)域(data region)和堆棧(stack region)逸尖。文本區(qū)域存儲處理器執(zhí)行的代碼属拾;數(shù)據(jù)區(qū)域存儲變量和進(jìn)程執(zhí)行期間使用的動態(tài)分配的內(nèi)存;堆棧區(qū)域存儲著活動過程調(diào)用的指令和本地變量冷溶。

特征
動態(tài)性:進(jìn)程的實(shí)質(zhì)是程序在多道程序系統(tǒng)中的一次執(zhí)行過程渐白,進(jìn)程是動態(tài)產(chǎn)生,動態(tài)消亡的逞频。
并發(fā)性:任何進(jìn)程都可以同其他進(jìn)程一起并發(fā)執(zhí)行
獨(dú)立性:進(jìn)程是一個能獨(dú)立運(yùn)行的基本單位纯衍,同時也是系統(tǒng)分配資源和調(diào)度的獨(dú)立單位;
異步性:由于進(jìn)程間的相互制約苗胀,使進(jìn)程具有執(zhí)行的間斷性襟诸,即進(jìn)程按各自獨(dú)立的、不可預(yù)知的速度向前推進(jìn)
結(jié)構(gòu)特征:進(jìn)程由程序基协、數(shù)據(jù)和進(jìn)程控制塊三部分組成歌亲。
多個不同的進(jìn)程可以包含相同的程序:一個程序在不同的數(shù)據(jù)集里就構(gòu)成不同的進(jìn)程,能得到不同的結(jié)果澜驮;但是執(zhí)行過程中陷揪,程序不能發(fā)生改變。

線程

線程杂穷,有時被稱為輕量級進(jìn)程(Lightweight Process悍缠,LWP),是程序執(zhí)行流的最小單元耐量。一個標(biāo)準(zhǔn)的線程由線程ID飞蚓,當(dāng)前指令指針(PC),寄存器集合和堆棧組成廊蜒。另外趴拧,線程是進(jìn)程中的一個實(shí)體溅漾,是被系統(tǒng)獨(dú)立調(diào)度和分派的基本單位,線程自己不擁有系統(tǒng)資源著榴,只擁有一點(diǎn)兒在運(yùn)行中必不可少的資源樟凄,但它可與同屬一個進(jìn)程的其它線程共享進(jìn)程所擁有的全部資源。

線程是程序中一個單一的順序控制流程兄渺。進(jìn)程內(nèi)有一個相對獨(dú)立的缝龄、可調(diào)度的執(zhí)行單元,是系統(tǒng)獨(dú)立調(diào)度和分派CPU的基本單位指令運(yùn)行時的程序的調(diào)度單位挂谍。在單個程序中同時運(yùn)行多個線程完成不同的工作叔壤,稱為多線程。

特點(diǎn)

在多線程OS中口叙,通常是在一個進(jìn)程中包括多個線程炼绘,每個線程都是作為利用CPU的基本單位,是花費(fèi)最小開銷的實(shí)體妄田。線程具有以下屬性俺亮。
1)輕型實(shí)體
線程中的實(shí)體基本上不擁有系統(tǒng)資源,只是有一點(diǎn)必不可少的疟呐、能保證獨(dú)立運(yùn)行的資源脚曾。
線程的實(shí)體包括程序、數(shù)據(jù)和TCB启具。線程是動態(tài)概念本讥,它的動態(tài)特性由線程控制塊TCB(Thread Control Block)描述。TCB包括以下信息:
(1)線程狀態(tài)鲁冯。
(2)當(dāng)線程不運(yùn)行時拷沸,被保存的現(xiàn)場資源。
(3)一組執(zhí)行堆棧薯演。
(4)存放每個線程的局部變量主存區(qū)撞芍。
(5)訪問同一個進(jìn)程中的主存和其它資源。
用于指示被執(zhí)行指令序列的程序計數(shù)器跨扮、保留局部變量序无、少數(shù)狀態(tài)參數(shù)和返回地址等的一組寄存器和堆棧。
2)獨(dú)立調(diào)度和分派的基本單位好港。
在多線程OS中愉镰,線程是能獨(dú)立運(yùn)行的基本單位米罚,因而也是獨(dú)立調(diào)度和分派的基本單位钧汹。由于線程很“輕”,故線程的切換非常迅速且開銷新荚瘛(在同一進(jìn)程中的)拔莱。
3)可并發(fā)執(zhí)行碗降。
在一個進(jìn)程中的多個線程之間,可以并發(fā)執(zhí)行塘秦,甚至允許在一個進(jìn)程中所有線程都能并發(fā)執(zhí)行讼渊;同樣,不同進(jìn)程中的線程也能并發(fā)執(zhí)行尊剔,充分利用和發(fā)揮了處理機(jī)與外圍設(shè)備并行工作的能力爪幻。
4)共享進(jìn)程資源。

在同一進(jìn)程中的各個線程须误,都可以共享該進(jìn)程所擁有的資源挨稿,這首先表現(xiàn)在:所有線程都具有相同的地址空間(進(jìn)程的地址空間),這意味著京痢,線程可以訪問該地址空間的每一個虛地址奶甘;此外,還可以訪問進(jìn)程所擁有的已打開文件祭椰、定時器臭家、信號量機(jī)構(gòu)等。由于同一個進(jìn)程內(nèi)的線程共享內(nèi)存和文件方淤,所以線程之間互相通信不必調(diào)用內(nèi)核钉赁。

進(jìn)程是資源單位,線程是執(zhí)行單位

協(xié)程

協(xié)程 携茂,又稱為微線程橄霉,它是實(shí)現(xiàn)多任務(wù)的另一種方式,只不過是比線程更小的執(zhí)行單元邑蒋。因?yàn)樗詭PU的上下文姓蜂,這樣只要在合適的時機(jī),我們可以把一個協(xié)程切換到另一個協(xié)程医吊。

通俗的理解: 在一個線程中的某個函數(shù)中钱慢,我們可以在任何地方保存當(dāng)前函數(shù)的一些臨時變量等信息,然后切換到另外一個函數(shù)中執(zhí)行卿堂,注意不是通過調(diào)用函數(shù)的方式做到的 束莫,并且切換的次數(shù)以及什么時候再切換到原來的函數(shù)都由開發(fā)者自己確定。

協(xié)程與線程的差異:
在實(shí)現(xiàn)多任務(wù)時, 線程切換從系統(tǒng)層面遠(yuǎn)不止保存和恢復(fù)CPU上下文這么簡單草描。操作系統(tǒng)為了程序運(yùn)行的高效性览绿,每個線程都有自己緩存Cache等等數(shù)據(jù),操作系統(tǒng)還會幫你做這些數(shù)據(jù)的恢復(fù)操作穗慕,所以線程的切換非常耗性能饿敲。但是協(xié)程的切換只是單純地操作CPU的上下文,所以一秒鐘切換個上百萬次系統(tǒng)都抗的住逛绵。

協(xié)程通過在線程中實(shí)現(xiàn)調(diào)度怀各,避免了陷入內(nèi)核級別的上下文切換造成的性能損失倔韭,進(jìn)而突破了線程在IO上的性能瓶頸。協(xié)程避免了無意義的調(diào)度瓢对,由此可以提高性能寿酌,但也因此,程序員必須自己承擔(dān)調(diào)度的責(zé)任硕蛹,同時醇疼,協(xié)程也失去了標(biāo)準(zhǔn)線程使用多CPU的能力

進(jìn)程,線程法焰,協(xié)程三者上下文切換比較

進(jìn)程 線程 協(xié)程
切換者 操作系統(tǒng) 操作系統(tǒng) 用戶(編程者/應(yīng)用程序)
切換時機(jī) 根據(jù)操作系統(tǒng)自己的切換策略僵腺,用戶不感知 根據(jù)操作系統(tǒng)自己的切換策略,用戶不感知 用戶自己的程序決定
切換內(nèi)容 頁全局目錄壶栋,內(nèi)核棧辰如,硬件上下文 內(nèi)核棧,硬件上下文 硬件上下文
切換內(nèi)容的保存 保存于內(nèi)核棧中 保存于內(nèi)核棧中 保存在用戶自己的變量(用戶椆笫裕或堆)
切換過程 用戶態(tài) - 內(nèi)核態(tài) - 用戶態(tài) 用戶態(tài) - 內(nèi)核態(tài) - 用戶態(tài) 用戶態(tài)(沒用進(jìn)入內(nèi)核態(tài))
切換效率

在python中琉兜,yield(生成器)可以很容易的實(shí)現(xiàn)上述的功能,從一個函數(shù)切換到另外一個函數(shù)毙玻。

Python的協(xié)程源于yield指令豌蟋。yield有兩個功能:

  • yield item用于產(chǎn)出一個值,反饋給next()的調(diào)用方桑滩。
  • 作出讓步梧疲,暫停執(zhí)行生成器,讓調(diào)用方繼續(xù)工作运准,直到需要使用另一個值時再調(diào)用next()幌氮。
import time

def task_1():
    while True:
        print("--This is task 1!--before")
        yield
        print("--This is task 1!--after")
        time.sleep(0.5)

def task_2():
    while True:
        print("--This is task 2!--before")
        yield
        print("--This is task 2!--after")
        time.sleep(0.5)
        
if __name__ == "__main__":
    t1 = task_1()  # 生成器對象
    t2 = task_2()
    # print(t1, t2)
    while True:
        next(t1)  # 1、喚醒生成器t1胁澳,執(zhí)行到y(tǒng)ield后该互,保存上下文,掛起任務(wù)韭畸;下次再次喚醒之后宇智,從yield繼續(xù)往下執(zhí)行
        print("\nThe main thread!\n")  # 2、繼續(xù)往下執(zhí)行
        next(t2)  # 3胰丁、喚醒生成器t2随橘,....

實(shí)現(xiàn)協(xié)作式多任務(wù),在Python3.5正式引入了 async/await表達(dá)式,使得協(xié)程正式在語言層面得到支持和優(yōu)化锦庸,大大簡化之前的yield寫法机蔗。

import asyncio
 
 
async def compute(x, y):
    print("Compute %s + %s ..." % (x, y))
    await asyncio.sleep(x + y)
    return x + y
 
 
async def print_sum(x, y):
    result = await compute(x, y)
    print("%s + %s = %s" % (x, y, result))
 
 
loop = asyncio.get_event_loop()
tasks = [print_sum(1, 2), print_sum(3, 4)]
loop.run_until_complete(asyncio.wait(tasks))
loop.close()


'''
1、event_loop 事件循環(huán):相當(dāng)于一個無限循環(huán),我們可以把一些函數(shù)注冊到這個事件循環(huán)上蜒车,當(dāng)滿足條件時讳嘱,就會調(diào)用對應(yīng)的處理方法幔嗦。
2酿愧、coroutine 協(xié)程:協(xié)程對象,只一個使用async關(guān)鍵字定義的函數(shù)邀泉,他的調(diào)用不會立即執(zhí)行函數(shù)嬉挡,而是會返回一個協(xié)程對象。協(xié)程對象需要注冊到事件循環(huán)中汇恤,由事件循環(huán)調(diào)用庞钢。
3、task 任務(wù):一個協(xié)程對象就是一個原生可以掛起的函數(shù)因谎,任務(wù)則是對協(xié)程的進(jìn)一步封裝基括,其中包含任務(wù)的各種狀態(tài)。
4财岔、future:代表將來執(zhí)行或沒有執(zhí)行的任務(wù)結(jié)果风皿。它與task沒有本質(zhì)的區(qū)別。
5匠璧、async/await 關(guān)鍵字:python3.5用于定義協(xié)程的關(guān)鍵字桐款,async定義一個協(xié)程,await用于掛起阻塞的異步調(diào)用接口夷恍。
6魔眨、run_until_complete:根據(jù)傳遞的參數(shù)的不同,返回的結(jié)果也有所不同
    ①酿雪、run_until_complete()傳遞的是一個協(xié)程對象或task對象遏暴,則返回他們finished的返回結(jié)果(前提是他們得       有return的結(jié)果,否則返回None)
    ②指黎、run_until_complete(asyncio.wait(多個協(xié)程對象或任務(wù)))拓挥,函數(shù)會返回一個元組包括(done,             pending),通過訪問done里的task對象袋励,獲取返回值
    ③侥啤、run_until_complete(asyncio.gather(多個協(xié)程對象或任務(wù))),函數(shù)會返回一個列表茬故,列表里面包括各個任     務(wù)的的返回結(jié)果盖灸,按順序排列
'''

協(xié)程是對線程的調(diào)度,yield類似惰性求值方式可以視為一種流程控制工具磺芭,
線程是內(nèi)核進(jìn)行搶占式的調(diào)度的赁炎,這樣就確保了每個線程都有執(zhí)行的機(jī)會。
而 coroutine 運(yùn)行在同一個線程中,由語言的運(yùn)行時中的 EventLoop(事件循環(huán))來進(jìn)行調(diào)度徙垫。
和大多數(shù)語言一樣讥裤,在 Python 中,協(xié)程的調(diào)度是非搶占式的姻报,也就是說一個協(xié)程必須主動讓出執(zhí)行機(jī)會己英,其他協(xié)程才有機(jī)會運(yùn)行。
讓出執(zhí)行的關(guān)鍵字就是 await吴旋。也就是說一個協(xié)程如果阻塞了损肛,持續(xù)不讓出 CPU,那么整個線程就卡住了荣瑟,沒有任何并發(fā)治拿。

Gevent

-----

Python通過yield提供了對協(xié)程的基本支持,但是不完全笆焰。而第三方的gevent為Python提供了比較完善的協(xié)程支持劫谅。

Gevent是一個第三方庫,可以輕松通過gevent實(shí)現(xiàn)并發(fā)同步或異步編程嚷掠,在gevent中主要用到的模式是Greenlet捏检,它是以C擴(kuò)展模塊形式接入Python的輕量級協(xié)程。Greenlet全部運(yùn)行在主程序操作系統(tǒng)的內(nèi)部叠国,被協(xié)作式調(diào)度未檩。

gevent其基本思想是:

當(dāng)一個greenlet遇到IO操作時,比如訪問網(wǎng)絡(luò)粟焊,就自動切換到其他的greenlet冤狡,等到IO操作完成,再在適當(dāng)?shù)臅r候切換回來繼續(xù)執(zhí)行项棠。由于IO操作非常耗時悲雳,經(jīng)常使程序處于等待狀態(tài),有了gevent為我們自動切換協(xié)程香追,就保證總有g(shù)reenlet在運(yùn)行合瓢,而不是等待IO。

由于切換是在IO操作時自動完成透典,所以gevent需要修改Python自帶的一些標(biāo)準(zhǔn)庫晴楔,這一過程在啟動時通過monkey patch完成:

from gevent import monkey; monkey.patch_socket()
import gevent

def f(n):
    for i in range(n):
        print gevent.getcurrent(), i

g1 = gevent.spawn(f, 5)
g2 = gevent.spawn(f, 5)
g3 = gevent.spawn(f, 5)
g1.join()
g2.join()
g3.join()
from gevent import monkey; monkey.patch_all()
import gevent
import urllib2

def f(url):
    print('GET: %s' % url)
    resp = urllib2.urlopen(url)
    data = resp.read()
    print('%d bytes received from %s.' % (len(data), url))

gevent.joinall([
        gevent.spawn(f, 'https://www.python.org/'),
        gevent.spawn(f, 'https://www.yahoo.com/'),
        gevent.spawn(f, 'https://github.com/'),
])

協(xié)程的同步寫法卻能擁有異步的性能

這個問題實(shí)際上在上面已經(jīng)得到解答了。雖然協(xié)程的使用是通過同步的寫法實(shí)現(xiàn)的峭咒。如果希望得到性能上的提升税弃,實(shí)際上還是通過異步回調(diào)的支持,只不過這個異步回調(diào)是底層模型上完成的凑队。所以上協(xié)程上關(guān)于同步異步的性能是建立在底層的異步模型上的则果。

協(xié)程占用的空間可以比線程小

協(xié)程是用戶定義的,所以在設(shè)計協(xié)程時,協(xié)程的最小空間占用可以比線程小很多西壮。在一個服務(wù)器很難創(chuàng)建10W個線程遗增,但是可以輕松的創(chuàng)建10W個協(xié)程。

協(xié)程切換比線程的切換更輕量

先控制一下變量款青,不考慮阻塞的問題
假設(shè)4核的cpu做修,100個任務(wù)并發(fā)執(zhí)行
(1)創(chuàng)建100個線程:100個線程參與cpu調(diào)度。
(2)創(chuàng)建10個線程可都,每個線程創(chuàng)建10個協(xié)程:10個線程參與cpu調(diào)度缓待,每個協(xié)程內(nèi)的10個協(xié)程由協(xié)程庫自己進(jìn)行調(diào)度蚓耽。

理論上渠牲,進(jìn)程擁有100個線程時,每個線程的時間片會比擁有10個線程時短步悠,也就意味著在相同時間里签杈,擁有100個線程時的上下文切換次數(shù)比擁有10個線程時多。

所以協(xié)程并發(fā)模型與多線程同步模型相比鼎兽,在一定條件下會減少線程切換次數(shù)答姥,但是增加了協(xié)程切換次數(shù),由于協(xié)程的切換是由協(xié)程庫調(diào)度的谚咬,所以很難說協(xié)程切換的代價比省去的線程切換代價小鹦付,合理的方式應(yīng)該是通過測試工具在具體的業(yè)務(wù)場景得出一個最好的平衡點(diǎn)。

將task函數(shù)封裝到Greenlet內(nèi)部線程的gevent.spawn择卦。 初始化的greenlet列表存放在數(shù)組threads中敲长,此數(shù)組被傳給gevent.joinall 函數(shù),后者阻塞當(dāng)前流程秉继,并執(zhí)行所有給定的greenlet祈噪。執(zhí)行流程只會在 所有g(shù)reenlet執(zhí)行完后才會繼續(xù)向下走。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末尚辑,一起剝皮案震驚了整個濱河市辑鲤,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌杠茬,老刑警劉巖月褥,帶你破解...
    沈念sama閱讀 222,252評論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異瓢喉,居然都是意外死亡宁赤,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,886評論 3 399
  • 文/潘曉璐 我一進(jìn)店門灯荧,熙熙樓的掌柜王于貴愁眉苦臉地迎上來礁击,“玉大人,你說我怎么就攤上這事《吡” “怎么了链烈?”我有些...
    開封第一講書人閱讀 168,814評論 0 361
  • 文/不壞的土叔 我叫張陵,是天一觀的道長挚躯。 經(jīng)常有香客問我强衡,道長,這世上最難降的妖魔是什么码荔? 我笑而不...
    開封第一講書人閱讀 59,869評論 1 299
  • 正文 為了忘掉前任漩勤,我火速辦了婚禮,結(jié)果婚禮上缩搅,老公的妹妹穿的比我還像新娘越败。我一直安慰自己,他們只是感情好硼瓣,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,888評論 6 398
  • 文/花漫 我一把揭開白布究飞。 她就那樣靜靜地躺著,像睡著了一般堂鲤。 火紅的嫁衣襯著肌膚如雪亿傅。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,475評論 1 312
  • 那天瘟栖,我揣著相機(jī)與錄音葵擎,去河邊找鬼。 笑死半哟,一個胖子當(dāng)著我的面吹牛酬滤,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播镜沽,決...
    沈念sama閱讀 41,010評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼敏晤,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了缅茉?” 一聲冷哼從身側(cè)響起嘴脾,我...
    開封第一講書人閱讀 39,924評論 0 277
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎蔬墩,沒想到半個月后译打,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,469評論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡拇颅,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,552評論 3 342
  • 正文 我和宋清朗相戀三年奏司,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片樟插。...
    茶點(diǎn)故事閱讀 40,680評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡韵洋,死狀恐怖竿刁,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情搪缨,我是刑警寧澤食拜,帶...
    沈念sama閱讀 36,362評論 5 351
  • 正文 年R本政府宣布,位于F島的核電站副编,受9級特大地震影響负甸,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜痹届,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,037評論 3 335
  • 文/蒙蒙 一呻待、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧队腐,春花似錦蚕捉、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,519評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽报破。三九已至悠就,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間充易,已是汗流浹背梗脾。 一陣腳步聲響...
    開封第一講書人閱讀 33,621評論 1 274
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留盹靴,地道東北人炸茧。 一個月前我還...
    沈念sama閱讀 49,099評論 3 378
  • 正文 我出身青樓,卻偏偏與公主長得像稿静,于是被迫代替她去往敵國和親梭冠。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,691評論 2 361