国产精品电影_久久视频免费_欧美日韩国产激情_成年人视频免费在线播放_日本久久亚洲电影_久久都是精品_66av99_九色精品美女在线_蜜臀a∨国产成人精品_冲田杏梨av在线_欧美精品在线一区二区三区_麻豆mv在线看

被遺棄在角落里的 Sync.Cond

開發(fā) 后端
Go 語(yǔ)言通過(guò) go 關(guān)鍵字開啟 goroutine 讓開發(fā)者可以輕松地實(shí)現(xiàn)并發(fā)編程,而并發(fā)程序的有效運(yùn)行,往往離不開 sync 包的保駕護(hù)航。

[[409469]]

本文轉(zhuǎn)載自微信公眾號(hào)「Golang技術(shù)分享」,作者機(jī)器鈴砍菜刀。轉(zhuǎn)載本文請(qǐng)聯(lián)系Golang技術(shù)分享公眾號(hào)。

Go 語(yǔ)言通過(guò) go 關(guān)鍵字開啟 goroutine 讓開發(fā)者可以輕松地實(shí)現(xiàn)并發(fā)編程,而并發(fā)程序的有效運(yùn)行,往往離不開 sync 包的保駕護(hù)航。目前,sync 包的賦能列表包括:sync.atomic 下的原子操作、sync.Map 并發(fā)安全 map、sync.Mutex 與 sync.RWMutex 提供的互斥鎖與讀寫鎖、sync.Pool 復(fù)用對(duì)象池、sync.Once 單例模式、 sync.Waitgroup 的多任務(wù)協(xié)作模式、sync.Cond 的監(jiān)視器模式。當(dāng)然,除了 sync 包,還有封裝層面更高的 channel 與 context。

要想寫出合格的 Go 程序,以上的這些并發(fā)原語(yǔ)是必須要掌握的。對(duì)于大多數(shù) Gopher 而言,sync.Cond 應(yīng)該是最為陌生,本文將一探究竟。

初識(shí) sync.Cond

sync.Cond 字面意義就是同步條件變量,它實(shí)現(xiàn)的是一種監(jiān)視器(Monitor)模式。

In concurrent programming(also known as parallel programming), a monitor is a synchronization construct that allows threads to have both mutual exclusion and the ability to wait (block) for a certain condition to become false.

對(duì)于 Cond 而言,它實(shí)現(xiàn)一個(gè)條件變量,是 goroutine 間等待和通知的點(diǎn)。條件變量與共享的數(shù)據(jù)隔離,它可以同時(shí)阻塞多個(gè) goroutine,直到另外的 goroutine 更改了條件變量,并通知喚醒阻塞著的一個(gè)或多個(gè) goroutine。

初次接觸的讀者,可能會(huì)不太明白,那么下面我們看一下 GopherCon 2018 上《Rethinking Classical Concurrency Patterns》 中的演示代碼例子。

  1.  1type Item = int 
  2.  2 
  3.  3type Queue struct { 
  4.  4   items     []Item 
  5.  5   itemAdded sync.Cond 
  6.  6} 
  7.  7 
  8.  8func NewQueue() *Queue { 
  9.  9   q := new(Queue) 
  10. 10   q.itemAdded.L = &sync.Mutex{} // 為 Cond 綁定鎖 
  11. 11   return q 
  12. 12} 
  13. 13 
  14. 14func (q *Queue) Put(item Item) { 
  15. 15   q.itemAdded.L.Lock() 
  16. 16   defer q.itemAdded.L.Unlock() 
  17. 17   q.items = append(q.items, item) 
  18. 18   q.itemAdded.Signal()        // 當(dāng) Queue 中加入數(shù)據(jù)成功,調(diào)用 Singal 發(fā)送通知 
  19. 19} 
  20. 20 
  21. 21func (q *Queue) GetMany(n int) []Item { 
  22. 22   q.itemAdded.L.Lock() 
  23. 23   defer q.itemAdded.L.Unlock() 
  24. 24   for len(q.items) < n {     // 等待 Queue 中有 n 個(gè)數(shù)據(jù) 
  25. 25      q.itemAdded.Wait()      // 阻塞等待 Singal 發(fā)送通知 
  26. 26   } 
  27. 27   items := q.items[:n:n] 
  28. 28   q.items = q.items[n:] 
  29. 29   return items 
  30. 30} 
  31. 31 
  32. 32func main() { 
  33. 33   q := NewQueue() 
  34. 34 
  35. 35   var wg sync.WaitGroup 
  36. 36   for n := 10; n > 0; n-- { 
  37. 37      wg.Add(1) 
  38. 38      go func(n int) { 
  39. 39         items := q.GetMany(n) 
  40. 40         fmt.Printf("%2d: %2d\n", n, items) 
  41. 41         wg.Done() 
  42. 42      }(n) 
  43. 43   } 
  44. 44 
  45. 45   for i := 0; i < 100; i++ { 
  46. 46      q.Put(i) 
  47. 47   } 
  48. 48 
  49. 49   wg.Wait() 
  50. 50} 

在這個(gè)例子中,Queue 是存儲(chǔ)數(shù)據(jù) Item 的結(jié)構(gòu)體,它通過(guò) Cond 類型的 itemAdded 來(lái)控制數(shù)據(jù)的輸入與輸出。可以注意到,這里通過(guò) 10 個(gè) goroutine 來(lái)消費(fèi)數(shù)據(jù),但它們所需的數(shù)據(jù)量并不相等,我們可以稱之為 batch,依次在 1-10 之間。之后,逐步添加 100 個(gè)數(shù)據(jù)至 Queue 中。最后,我們能夠看到 10 個(gè) gotoutine 都能被喚醒,得到它想要的數(shù)據(jù)。

程序運(yùn)行結(jié)果如下

  1. 1 6: [ 7  8  9 10 11 12] 
  2. 2 5: [50 51 52 53 54] 
  3. 3 9: [14 15 16 17 18 19 20 21 22] 
  4. 4 1: [13] 
  5. 5 2: [33 34] 
  6. 6 4: [35 36 37 38] 
  7. 7 3: [39 40 41] 
  8. 8 7: [ 0  1  2  3  4  5  6] 
  9. 9 8: [42 43 44 45 46 47 48 49] 
  10. 010: [23 24 25 26 27 28 29 30 31 32] 

當(dāng)然,程序每次運(yùn)行結(jié)果都不會(huì)相同,以上輸出只是某一種情況。

sync.Cond 實(shí)現(xiàn)

在 $GOPATH/src/sync/cond.go 中,Cond 的結(jié)構(gòu)體定義如下

  1. 1type Cond struct { 
  2. 2   noCopy noCopy 
  3. 3   L Locker 
  4. 4   notify  notifyList 
  5. 5   checker copyChecker 
  6. 6} 

其中,noCopy 與 checker 字段均是為了避免 Cond 在使用過(guò)程中被復(fù)制,詳見(jiàn)小菜刀的 《no copy 機(jī)制》 一文。

L 是 Locker 接口,一般該字段的實(shí)際對(duì)象是 *RWmutex 或者 *Mutex。

  1. 1type Locker interface { 
  2. 2   Lock() 
  3. 3   Unlock() 
  4. 4} 

notifyList 記錄的是一個(gè)基于票號(hào)的通知列表,這里初次看注釋看不懂沒(méi)關(guān)系,和下文來(lái)回連貫著看。

  1. 1type notifyList struct { 
  2. 2   wait   uint32         // 用于記錄下一個(gè)等待者 waiter 的票號(hào) 
  3. 3   notify uint32         // 用于記錄下一個(gè)應(yīng)該被通知的 waiter 的票號(hào) 
  4. 4   lock   uintptr        // 內(nèi)部鎖 
  5. 5   head   unsafe.Pointer // 指向等待者 waiter 的隊(duì)列隊(duì)頭 
  6. 6   tail   unsafe.Pointer // 指向等待者 waiter 的隊(duì)列隊(duì)尾 
  7. 7} 

其中,head 與 tail 是指向 sudog 結(jié)構(gòu)體的指針,sudog 是代表的處于等待列表的 goroutine,它本身就是雙向鏈表。值得一提的是,在 sudog 中有一個(gè)字段 ticket 就是用于給當(dāng)前 goroutine 記錄票號(hào)使用的。

Cond 實(shí)現(xiàn)的核心模式為票務(wù)系統(tǒng)(ticket system),每一個(gè)想要來(lái)買票的 goroutine (調(diào)用Cond.Wait())我們稱之為 waiter,票務(wù)系統(tǒng)會(huì)給每個(gè) waiter 分配一個(gè)取票碼,等供票方有該取票碼的號(hào)時(shí),就會(huì)喚醒 waiter。賣票的 goroutine 有兩種,第一種是調(diào)用 Cond.Signal() 的,它會(huì)按照票號(hào)喚醒一個(gè)買票的 waiter (如果有的話),第二種是調(diào)用 Cond.Broadcast() 的,它會(huì)通知喚醒所有的阻塞 waiter。為了方便讀者能夠比較輕松地理解票務(wù)系統(tǒng),下面我們給出圖解示例。

在 上文中,我們知道 Cond 字段中 notifyList 結(jié)構(gòu)體是一個(gè)記錄票號(hào)的通知列表。這里將 notifyList 比作排隊(duì)取票買電影票,當(dāng) G1 通過(guò) Wait 來(lái)買票時(shí),發(fā)現(xiàn)此時(shí)并沒(méi)有票可買,因此他只能阻塞等待有票之后的通知,此時(shí)他手上已經(jīng)取得了專屬取票碼 0。同樣的,G2 和 G3 也同樣無(wú)票可買,它們分別取到了自己的取票碼 1和 2。而 G4 是電影票提供商,它是賣票的,它通過(guò)兩次 Signal 先后帶來(lái)了兩張票,按照票號(hào)順序依次通知了 G1 和 G2 來(lái)取票,并把 notify 更新為了最新的 1。G5 也是買票的,它發(fā)現(xiàn)此時(shí)已經(jīng)無(wú)票可買了,拿了自己的取票碼 3 ,就阻塞等待了。G6 是個(gè)大票商,它通過(guò) Broadcast 可以滿足所有正在等待的買票者都買到票,此時(shí)等待的是 G3 和 G5,因此他直接喚醒了 G3 和 G5,并將 notify 更新到和 wait 值相等。

理解了上述取票系統(tǒng)的運(yùn)作原理后,我們下面來(lái)看 Cond 包下四個(gè)實(shí)際對(duì)外方法函數(shù)的實(shí)現(xiàn)。

NewCond 方法

  1. 1func NewCond(l Locker) *Cond { 
  2. 2   return &Cond{L: l} 
  3. 3} 

用于初始化 Cond 對(duì)象,就是初始化控制鎖。

Cond.Wait 方法

  1. 1func (c *Cond) Wait() { 
  2. 2   c.checker.check() 
  3. 3   t := runtime_notifyListAdd(&c.notify) 
  4. 4   c.L.Unlock() 
  5. 5   runtime_notifyListWait(&c.notify, t) 
  6. 6   c.L.Lock() 
  7. 7} 

runtime_notifyListAdd 的實(shí)現(xiàn)在 runtime/sema.go 的 notifyListAdd ,它用于原子性地增加等待者的 waiter 票號(hào),并返回當(dāng)前 goroutine 應(yīng)該取的票號(hào)值 t 。runtime_notifyListWait 的實(shí)現(xiàn)在runtime/sema.go 的 notifyListWait,它會(huì)嘗試去比較此時(shí) goroutine 的應(yīng)取票號(hào) t 與 notify 中記錄的當(dāng)前應(yīng)該被通知的票號(hào)。如果 t 小于當(dāng)前票號(hào),那么直接能得到返回,否則將會(huì)則塞等待,通知取號(hào)。

同時(shí),這里需要注意的是,由于在進(jìn)入 runtime_notifyListWait 時(shí),當(dāng)前 goroutine 通過(guò) c.L.Unlock() 將鎖解了,這就意味著有可能會(huì)有多個(gè) goroutine 來(lái)讓條件發(fā)生變化。那么,當(dāng)前 goroutine 是不能保證在 runtime_notifyListWait 返回后,條件就一定是真的,因此需要循環(huán)判斷條件。正確的 Wait 使用姿勢(shì)如下:

  1. 1//    c.L.Lock() 
  2. 2//    for !condition() { 
  3. 3//        c.Wait() 
  4. 4//    } 
  5. 5//    ... make use of condition ... 
  6. 6//    c.L.Unlock() 

Cond.Signal 方法

  1. 1func (c *Cond) Signal() { 
  2. 2   c.checker.check() 
  3. 3   runtime_notifyListNotifyOne(&c.notify) 
  4. 4} 

runtime_notifyListNotifyOne 的詳細(xì)實(shí)現(xiàn)在 runtime/sema.go 的 notifyListNotifyOne,它的目的就是通知 waiter 取票。具體操作是:如果在上一次通知取票之后沒(méi)有新的 waiter 取票者,那么該函數(shù)會(huì)直接返回。否則,它會(huì)將取票號(hào) +1,并通知喚醒等待取票的 waiter。

需要注意的是,調(diào)用 Signal 方法時(shí),并不需要持有 c.L 鎖。

Cond.Broadcast 方法

  1. 1func (c *Cond) Broadcast() { 
  2. 2   c.checker.check() 
  3. 3   runtime_notifyListNotifyAll(&c.notify) 
  4. 4} 

runtime_notifyListNotifyAll 的詳細(xì)實(shí)現(xiàn)在 runtime/sema.go 的 notifyListNotifyAll,它會(huì)通知喚醒所有的 waiter,并將 notify 值置為 和 wait 值相等。調(diào)用 Broadcast 方法時(shí),也不需要持有 c.L 鎖。

討論

在 $GOPATH/src/sync/cond.go 下,我們可以發(fā)現(xiàn)其代碼量非常之少,但它呈現(xiàn)的只是核心邏輯,其實(shí)現(xiàn)細(xì)節(jié)位于 runtime/sema.go 之中,依賴的是 runtime 層的調(diào)度原語(yǔ),對(duì)細(xì)節(jié)感興趣的讀者可以深入學(xué)習(xí)。

問(wèn)題來(lái)了,為什么在日常開發(fā)中,我們很少會(huì)使用到 sync.Cond ?

無(wú)效喚醒

前文中我們提到,使用 Cond.Wait 正確姿勢(shì)如下

  1. 1    c.L.Lock() 
  2. 2    for !condition() { 
  3. 3        c.Wait() 
  4. 4    } 
  5. 5    ... make use of condition ... 
  6. 6    c.L.Unlock() 

以文章開頭的例子而言,如果在每次調(diào)用 Put 方法時(shí),使用 Broadcast 方法喚醒所有的 waiter,那么很大概率上被喚醒的 waiter 醒來(lái)發(fā)現(xiàn)條件并不滿足,又會(huì)重新進(jìn)入等待。盡管是調(diào)用 Signal 方法喚醒指定的 waiter,但是它也不能保證喚醒的 waiter 條件一定滿足。因此,在實(shí)際的使用中,我們需要盡量保證喚醒操作是有效地,為了做到這點(diǎn),代碼的復(fù)雜度難免會(huì)增加。

  • 饑餓問(wèn)題

還是以文章開頭例子為例,如果同時(shí)有多個(gè) goroutine 執(zhí)行 GetMany(3) 和 GetMany(3000),執(zhí)行 GetMany(3) 與執(zhí)行 GetMany(3000) 的 goroutine 被喚醒的概率是一樣的,但是由于 GetMany(3) 只需要 3個(gè)數(shù)據(jù)就能滿足條件,那么如果一直存在 GetMany(3) 的 goroutine,執(zhí)行 GetMany(3000) 的 goroutine 將永遠(yuǎn)拿不到數(shù)據(jù),一直被無(wú)效喚醒。

  • 不能響應(yīng)其他事件

條件變量的意義在于讓 goroutine 等待某種條件發(fā)生時(shí)進(jìn)入睡眠狀態(tài)。但是這會(huì)讓 goroutine 在等待條件時(shí),可能會(huì)錯(cuò)過(guò)一些需要注意的其他事件。例如,調(diào)用 Cond.Wait 的函數(shù)中包含了 context 上下文,當(dāng) context 傳來(lái)取消信號(hào)時(shí),它并不能像我們期望的一樣,獲取到取消信號(hào)并退出。Cond 的使用,讓我們不能同時(shí)選擇(select)條件和其他事件。

  • 可替代性

通過(guò)對(duì) sync.Cond 幾個(gè)對(duì)外方法的分析,我們不難看到,它的使用場(chǎng)景是可以被 channel 所代替的,但是這也會(huì)增加代碼的復(fù)雜性。上文中的例子,可以使用 channel 改寫如下。

  1.  1type Item = int 
  2.  2 
  3.  3type waiter struct { 
  4.  4    n int 
  5.  5    c chan []Item 
  6.  6} 
  7.  7 
  8.  8type state struct { 
  9.  9    items []Item 
  10. 10    wait  []waiter 
  11. 11} 
  12. 12 
  13. 13type Queue struct { 
  14. 14    s chan state 
  15. 15} 
  16. 16 
  17. 17func NewQueue() *Queue { 
  18. 18    s := make(chan state, 1) 
  19. 19    s <- state{} 
  20. 20    return &Queue{s} 
  21. 21} 
  22. 22 
  23. 23func (q *Queue) Put(item Item) { 
  24. 24    s := <-q.s 
  25. 25    s.items = append(s.items, item) 
  26. 26    for len(s.wait) > 0 { 
  27. 27        w := s.wait[0] 
  28. 28        if len(s.items) < w.n { 
  29. 29            break 
  30. 30        } 
  31. 31        w.c <- s.items[:w.n:w.n] 
  32. 32        s.items = s.items[w.n:] 
  33. 33        s.wait = s.wait[1:] 
  34. 34    } 
  35. 35    q.s <- s 
  36. 36} 
  37. 37 
  38. 38func (q *Queue) GetMany(n int) []Item { 
  39. 39    s := <-q.s 
  40. 40    if len(s.wait) == 0 && len(s.items) >= n { 
  41. 41        items := s.items[:n:n] 
  42. 42        s.items = s.items[n:] 
  43. 43        q.s <- s 
  44. 44        return items 
  45. 45    } 
  46. 46 
  47. 47    c := make(chan []Item) 
  48. 48    s.wait = append(s.wait, waiter{n, c}) 
  49. 49    q.s <- s 
  50. 50 
  51. 51    return <-c 
  52. 52} 

 

最后,雖然在上文的討論中都是列出的 sync.Cond 潛在問(wèn)題,但是如果開發(fā)者能夠在使用中考慮到以上的幾點(diǎn)問(wèn)題,對(duì)于監(jiān)視器模型的實(shí)現(xiàn)而言,在代碼的語(yǔ)義邏輯上,sync.Cond 的使用會(huì)比 channel 的模式更易理解和維護(hù)。記住一點(diǎn),通俗易懂的代碼模型總是比深?yuàn)W的炫技要接地氣。

 

責(zé)任編輯:武曉燕 來(lái)源: Golang技術(shù)分享
相關(guān)推薦

2023-06-26 08:28:35

Sync.CondGolang

2021-05-21 08:21:57

Go語(yǔ)言基礎(chǔ)技術(shù)

2015-07-20 16:58:35

短信微信

2023-11-28 08:01:48

互斥鎖共享資源

2021-03-15 07:12:15

Windows10操作系統(tǒng)21H2

2015-10-21 16:11:39

WP支付寶

2019-06-28 10:55:04

預(yù)熱高并發(fā)并發(fā)高

2020-04-26 17:04:31

安全機(jī)器學(xué)習(xí)數(shù)據(jù)

2010-12-24 14:02:18

云供應(yīng)商

2012-10-29 10:20:33

Google數(shù)據(jù)中心云計(jì)算

2025-08-20 09:17:41

2017-12-20 09:32:27

網(wǎng)絡(luò)安全防火墻動(dòng)態(tài)安全

2017-12-12 15:58:23

2020-11-02 12:45:18

人工智能

2020-06-02 09:22:45

腳本CPUDDG

2012-02-15 15:18:07

2011-03-15 08:54:35

程序員人才

2013-01-28 16:51:45

2024-12-16 08:30:00

JVMJava虛擬機(jī)Java

2017-06-29 14:47:57

點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)

日韩一区av在线| 三区精品视频观看| 一区二区欧美视频| 激情婷婷久久| 美女羞羞视频在线观看| 黄色免费视频大全| 久久av.com| 国产一区二区三区三区在线观看 | 国产小视频免费在线观看| 豆国产97在线| 国产成人高潮免费观看精品| 日韩视频一区在线观看| 午夜av区久久| 亚洲精品社区| 久久精品久久久| 黄瓜视频免费观看在线观看www| 国内免费精品永久在线视频| 精品美女久久久久久免费| 亚洲r级在线视频| 久久精品免费观看| 性欧美xxxx大乳国产app| 免费h在线看| 一个人看的www视频在线免费观看| 欧美黄色免费网址| 精品国产免费av| 7777精品久久久大香线蕉小说| 久久综合网hezyo| 在线观看视频欧美| 91麻豆精品秘密| 欧美午夜电影在线观看 | 狠狠色综合色综合网络| 羞羞视频在线观看欧美| 极品国产人妖chinesets亚洲人妖| www.久久爱.com| 国产剧情在线观看| 在线观看免费黄色| 91.com在线| 国产一区一一区高清不卡| 亚洲欧美日韩国产中文| 亚洲免费高清视频在线| 欧美国产精品中文字幕| 午夜免费久久看| 欧美尤物巨大精品爽| 涩爱av色老久久精品偷偷鲁 | 日本成人看片网址| 二区在线播放| 国产乱人伦真实精品视频| 欧美高清在线观看| 国产裸体免费无遮挡| 亚欧日韩另类中文欧美| 91麻豆6部合集magnet| 久久久久成人网| 高清一级毛片视频| 一本久久青青| 欧美日韩一级视频| 亚洲 欧洲 日韩| 国产剧情一区二区在线观看| 欧美国产精品劲爆| 91欧美激情另类亚洲| av在线免费网址| 亚洲国产精品尤物yw在线观看| 久久久久久欧美精品色一二三四| 麻豆网站在线免费观看| 成人爽a毛片一区二区免费| 久久久天堂国产精品女人| 在线免费福利| 国产一区二区三区免费看| 午夜精品美女自拍福到在线| av成人手机在线| 99久久免费视频.com| 青草青草久热精品视频在线观看| 毛片在线能看| 国产福利一区二区三区在线视频| 国产综合在线视频| 神马午夜伦理不卡| 中文字幕欧美一| 久久精品二区| 国产日韩在线看| 色是在线视频| 婷婷夜色潮精品综合在线| 久久久久久久久久久一区| 成人黄色图片网站| 欧美在线看片a免费观看| 国产成人免费视频网站| 国产精品av网站| 电影网一区二区| 欧美系列在线观看| 黄大色黄女片18第一次| 丝袜亚洲另类欧美综合| 午夜精品久久17c| 成全电影大全在线观看| 亚洲va国产天堂va久久en| 无码中文字幕色专区| 欧美日本中文| 国产精品久久久久久久久久| 日韩黄色碟片| 精品国产一区久久| 最近97中文超碰在线| 国产成+人+日韩+欧美+亚洲| 成人av免费电影| 色婷婷狠狠五月综合天色拍| 在线观看精品国产视频| 含羞草www国产在线视频| 中文字幕综合网| 成人午夜免费在线| 久久精品国产**网站演员| 国产精品乱码| 日韩精品一区二区久久| 久久免费视频在线| 人人精品久久| 国产亚洲人成a一在线v站| 狂野欧美性猛交xxxxx视频| 色哟哟日韩精品| 黄色在线小视频| 欧美国产视频在线| 超碰成人免费在线| 狠狠色狠狠色综合| 你懂的视频在线一区二区| 国产精品久久久久久久免费软件 | 免费av手机在线观看| 麻豆成人av在线| 三级网在线观看| 一区二区三区在线免费视频| 国产精品三级a三级三级午夜 | 日本福利视频| 中文字幕二三区不卡| 一区二区三区免费播放| 国产午夜精品一区二区| 欧美日韩国产精品激情在线播放| 成人免费高清在线| 最新中文字幕2018| 国产婷婷一区二区| 亚洲精品自拍网| 亚洲一区二区3| 四虎免费av| 精品久久久国产精品999| 亚洲女人视频| 欧美亚洲综合网| heyzo在线播放| 在线播放国产精品| 久久9999免费视频| 国产成人亚洲综合91| 欧美成人国产| 国产精品久久久影院| 17c精品麻豆一区二区免费| 成人短剧在线观看| 欧洲av一区二区嗯嗯嗯啊| 免费**毛片在线| 中文字幕久热精品在线视频| 国产精品久久久久av蜜臀| 国产精品久久久久久网站| 日本成人黄色免费看| 亚洲天堂激情| 国产精品视频二| 91精品国产福利在线观看麻豆| 成年人视频在线看| 亚洲国产欧美一区二区三区丁香婷| 亚洲麻豆精品| 欧美精品在线视频观看| 日韩在线一区二区三区| 男人揉女人奶房视频60分 | videos性欧美另类高清| 日韩暖暖在线视频| 国产一区二区看久久| 日韩在线综合网| 亚洲成人激情综合网| 国产在线观看免费网站| 国产三区在线观看| 久久精品国语| 久久9精品区-无套内射无码| 不卡一区二区在线| 亚洲夫妻av| www.日韩.com| 亚洲美女啪啪| 色佬视频在线观看| 欧美在线视频日韩| 免费观看亚洲视频大全| 韩国一区二区三区美女美女秀| 国产在线一区观看| 福利在线午夜| 亚洲国产精品va在线看黑人| 僵尸再翻生在线观看免费国语| 欧美日韩大片一区二区三区| **性色生活片久久毛片| 欧美视频免费看| 国产精品美女xx| www.一区二区| 在线免费中文字幕| 欧美一三区三区四区免费在线看 | 色婷婷精品久久二区二区蜜臂av| av网站在线免费观看| 亚洲性视频网址| 亚洲人成亚洲精品| 国产精品亚洲综合| 国产亚洲精品bt天堂精选| 日韩a**中文字幕| 欧洲精品久久| 欧美在线免费观看视频| 91精品一区二区三区综合| 中文字幕免费在线观看|