精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

被遺棄在角落里的 Sync.Cond

開發 后端
Go 語言通過 go 關鍵字開啟 goroutine 讓開發者可以輕松地實現并發編程,而并發程序的有效運行,往往離不開 sync 包的保駕護航。

[[409469]]

本文轉載自微信公眾號「Golang技術分享」,作者機器鈴砍菜刀。轉載本文請聯系Golang技術分享公眾號。

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

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

初識 sync.Cond

sync.Cond 字面意義就是同步條件變量,它實現的是一種監視器(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.

對于 Cond 而言,它實現一個條件變量,是 goroutine 間等待和通知的點。條件變量與共享的數據隔離,它可以同時阻塞多個 goroutine,直到另外的 goroutine 更改了條件變量,并通知喚醒阻塞著的一個或多個 goroutine。

初次接觸的讀者,可能會不太明白,那么下面我們看一下 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()        // 當 Queue 中加入數據成功,調用 Singal 發送通知 
  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 個數據 
  25. 25      q.itemAdded.Wait()      // 阻塞等待 Singal 發送通知 
  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} 

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

程序運行結果如下

  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] 

當然,程序每次運行結果都不會相同,以上輸出只是某一種情況。

sync.Cond 實現

在 $GOPATH/src/sync/cond.go 中,Cond 的結構體定義如下

  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 在使用過程中被復制,詳見小菜刀的 《no copy 機制》 一文。

L 是 Locker 接口,一般該字段的實際對象是 *RWmutex 或者 *Mutex。

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

notifyList 記錄的是一個基于票號的通知列表,這里初次看注釋看不懂沒關系,和下文來回連貫著看。

  1. 1type notifyList struct { 
  2. 2   wait   uint32         // 用于記錄下一個等待者 waiter 的票號 
  3. 3   notify uint32         // 用于記錄下一個應該被通知的 waiter 的票號 
  4. 4   lock   uintptr        // 內部鎖 
  5. 5   head   unsafe.Pointer // 指向等待者 waiter 的隊列隊頭 
  6. 6   tail   unsafe.Pointer // 指向等待者 waiter 的隊列隊尾 
  7. 7} 

其中,head 與 tail 是指向 sudog 結構體的指針,sudog 是代表的處于等待列表的 goroutine,它本身就是雙向鏈表。值得一提的是,在 sudog 中有一個字段 ticket 就是用于給當前 goroutine 記錄票號使用的。

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

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

理解了上述取票系統的運作原理后,我們下面來看 Cond 包下四個實際對外方法函數的實現。

NewCond 方法

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

用于初始化 Cond 對象,就是初始化控制鎖。

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 的實現在 runtime/sema.go 的 notifyListAdd ,它用于原子性地增加等待者的 waiter 票號,并返回當前 goroutine 應該取的票號值 t 。runtime_notifyListWait 的實現在runtime/sema.go 的 notifyListWait,它會嘗試去比較此時 goroutine 的應取票號 t 與 notify 中記錄的當前應該被通知的票號。如果 t 小于當前票號,那么直接能得到返回,否則將會則塞等待,通知取號。

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

  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 的詳細實現在 runtime/sema.go 的 notifyListNotifyOne,它的目的就是通知 waiter 取票。具體操作是:如果在上一次通知取票之后沒有新的 waiter 取票者,那么該函數會直接返回。否則,它會將取票號 +1,并通知喚醒等待取票的 waiter。

需要注意的是,調用 Signal 方法時,并不需要持有 c.L 鎖。

Cond.Broadcast 方法

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

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

討論

在 $GOPATH/src/sync/cond.go 下,我們可以發現其代碼量非常之少,但它呈現的只是核心邏輯,其實現細節位于 runtime/sema.go 之中,依賴的是 runtime 層的調度原語,對細節感興趣的讀者可以深入學習。

問題來了,為什么在日常開發中,我們很少會使用到 sync.Cond ?

無效喚醒

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

  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() 

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

  • 饑餓問題

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

  • 不能響應其他事件

條件變量的意義在于讓 goroutine 等待某種條件發生時進入睡眠狀態。但是這會讓 goroutine 在等待條件時,可能會錯過一些需要注意的其他事件。例如,調用 Cond.Wait 的函數中包含了 context 上下文,當 context 傳來取消信號時,它并不能像我們期望的一樣,獲取到取消信號并退出。Cond 的使用,讓我們不能同時選擇(select)條件和其他事件。

  • 可替代性

通過對 sync.Cond 幾個對外方法的分析,我們不難看到,它的使用場景是可以被 channel 所代替的,但是這也會增加代碼的復雜性。上文中的例子,可以使用 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 潛在問題,但是如果開發者能夠在使用中考慮到以上的幾點問題,對于監視器模型的實現而言,在代碼的語義邏輯上,sync.Cond 的使用會比 channel 的模式更易理解和維護。記住一點,通俗易懂的代碼模型總是比深奧的炫技要接地氣。

 

責任編輯:武曉燕 來源: Golang技術分享
相關推薦

2023-06-26 08:28:35

Sync.CondGolang

2021-05-21 08:21:57

Go語言基礎技術

2015-07-20 16:58:35

短信微信

2023-11-28 08:01:48

互斥鎖共享資源

2021-03-15 07:12:15

Windows10操作系統21H2

2015-10-21 16:11:39

WP支付寶

2019-06-28 10:55:04

預熱高并發并發高

2020-04-26 17:04:31

安全機器學習數據

2012-10-29 10:20:33

Google數據中心云計算

2010-12-24 14:02:18

云供應商

2017-12-20 09:32:27

網絡安全防火墻動態安全

2017-12-12 15:58:23

2020-11-02 12:45:18

人工智能

2025-08-20 09:17:41

2020-06-02 09:22:45

腳本CPUDDG

2012-02-15 15:18:07

2011-03-15 08:54:35

程序員人才

2009-09-11 09:55:19

谷歌遺棄互聯網服務

2013-01-28 16:51:45

2024-12-16 08:30:00

JVMJava虛擬機Java
點贊
收藏

51CTO技術棧公眾號

搞黄视频免费在线观看| 国产精品一区二区三区四| 豆花视频一区| 亚洲午夜私人影院| 欧美日韩国产一二| 国产一区二区三区四区视频| 91精品国产成人观看| 精品国内片67194| 日日碰狠狠躁久久躁婷婷| 91在线高清| 国产成人精品www牛牛影视| 欧美中文字幕在线观看| 黑鬼狂亚洲人videos| 日韩大片在线免费观看| 3atv在线一区二区三区| 日本免费不卡一区二区| 黄色精品免费看| 久久精品在线观看| 不卡视频一区| 91成品人影院| 久久精品亚洲一区二区| 欧美极品少妇与黑人| 国精产品一区一区| 欧美**字幕| 欧美精品一区二区三区很污很色的| 97公开免费视频| 国产不卡人人| 亚洲最大的成人av| 国产又黄又爽免费视频| 精品999视频| av成人免费在线观看| 91精品在线影院| 综合久久中文字幕| 久久精品男女| 亚洲一区在线视频观看| 欧美高清性hdvideosex| 妺妺窝人体色www在线小说| www在线免费观看视频| 日本一区二区成人| 欧美激情视频一区二区三区| 欧美综合视频在线| 国产精品影音先锋| 成人激情视频在线观看| 夜夜躁狠狠躁日日躁av| 日韩制服丝袜先锋影音| 日韩av电影在线网| 日韩毛片一区二区三区| 国产欧美高清| 91国产视频在线播放| 国产一级做a爰片在线看免费| 亚洲欧美网站在线观看| 精品激情国产视频| 在线观看黄网址| 亚洲成av人电影| 久久中文字幕一区| 2018天天弄| 国产精品v日韩精品v欧美精品网站| 久久最新资源网| 亚洲天堂一级片| 中文字幕亚洲精品乱码| 欧美久久久精品| 国产精品成人国产乱| 亚洲茄子视频| 欧美一区二区三区图| 国产又黄又粗又爽| 日韩精品一二区| 国产在线视频91| 国产99对白在线播放| 黄页网站大全一区二区| 亚洲一区二区三区成人在线视频精品 | 成人乱码手机视频| 91精品国产乱码久久蜜臀| 美女被艹视频网站| xxxxxhd亚洲人hd| 日韩av网址在线| 亚洲精品成人无码熟妇在线| 国产99精品| 日韩有码视频在线| 欧美精品久久久久性色| 亚洲经典自拍| 国产脚交av在线一区二区| 中文字幕欧美色图| 国产成人免费在线观看| 极品尤物一区二区三区| 国产原创av在线| 中文字幕亚洲在| 日本国产中文字幕| 国产高清不卡| 制服.丝袜.亚洲.另类.中文| 激情小说欧美色图| 校花撩起jk露出白色内裤国产精品| 亚洲男人天堂2019| 色偷偷www8888| 在线视频日韩| 国产精自产拍久久久久久| www.av网站| 国产日韩欧美亚洲| 青青在线视频免费观看| 电影一区二区三区| 日韩欧美国产综合一区| 国产av自拍一区| 欧美日韩1080p| 国产精品av在线| 亚洲国产精品久久久久爰性色| 久久婷婷成人综合色| 色哺乳xxxxhd奶水米仓惠香| 亚洲一二三四| 欧美va日韩va| 日本人亚洲人jjzzjjz| 在线国产精品一区| 91免费精品视频| 美国成人毛片| 亚洲成av人片一区二区三区| 亚洲xxx在线观看| 天堂综合网久久| 欧美成人合集magnet| 蜜臀99久久精品久久久久小说| 国产大陆a不卡| 爱爱爱视频网站| 精品3atv在线视频| 亚洲第一精品夜夜躁人人爽| 久久国产高清视频| 日本在线不卡一区| 精品一区二区国产| a在线免费观看| 欧美卡1卡2卡| 欧美另类z0zx974| 国产亚洲综合精品| 国产亚洲欧美一区二区三区| 在线观看中文| 欧美日韩情趣电影| 麻豆av免费观看| 99在线观看免费视频精品观看| 亚洲最大福利视频网| 欧美激情午夜| 欧美三级在线播放| 欧美极品jizzhd欧美18| 天堂av在线一区| 欧美一级二级三级九九九| 欧美6一10sex性hd| 日韩免费电影一区| 美女的奶胸大爽爽大片| 国产伦理精品不卡| 五月天激情图片| 欧美区一区二区| 久久这里有精品| 国产三级三级在线观看| 亚洲欧洲制服丝袜| 能看毛片的网站| 欧美在线影院| 99视频国产精品免费观看| 伊人影院蕉久影院在线播放| 欧美成人欧美edvon| 久久久久久久久毛片| 国产乱码精品1区2区3区| 日韩精品第1页| 亚洲成人五区| 性色av一区二区三区| 亚州男人的天堂| 日韩黄色大片网站| 亚洲综合色区另类av| 57pao国产成永久免费视频| 色婷婷亚洲mv天堂mv在影片| 国产欧美久久久久久| 国产美女av在线| 日韩视频一区二区在线观看| 国产一级片网址| 93久久精品日日躁夜夜躁欧美| 免费在线观看日韩视频| 国内黄色精品| 91精品视频在线播放| 久久青青色综合| 日韩精品久久久久久福利| 一级黄色大片视频| 国产精品高潮呻吟久久| 性鲍视频在线观看| 在线中文一区| 国产欧美在线一区二区| 外国成人直播| 久久久av免费| 天堂在线视频网站| 在线观看91精品国产入口| 亚洲AV成人无码精电影在线| 成人福利在线看| 成人黄色一区二区| 亚洲欧美在线专区| 精品一区二区三区国产| 成人毛片免费| 久久久久久噜噜噜久久久精品| 欧美在线一卡| 91精品在线观看入口| 亚洲精品1区2区3区| 国产精品国产三级国产aⅴ原创 | 忘忧草在线www成人影院| 精品国产一区二区三区久久狼5月| www.色视频| 在线观看欧美黄色| 久久机热这里只有精品| 国产午夜精品美女毛片视频| 日韩久久久久久久久久久| 西西裸体人体做爰大胆久久久| 在线无限看免费粉色视频| 色愁久久久久久| 91在线视频精品| 韩国三级一区| 久久免费视频网站| 麻豆网站在线观看| 亚洲欧美中文字幕| 亚洲精品久久久久avwww潮水| 欧美亚洲综合色| 日韩激情在线播放| 一区二区三区中文字幕精品精品 | 中文字幕avav| 青娱乐精品视频| 无码播放一区二区三区| 欧美激情视频一区二区三区在线播放 | 国产91对白在线播放| 国产写真视频在线观看| 亚洲一区二区国产| 午夜成人免费影院| 91精品国产高清一区二区三区| 亚洲色成人www永久网站| 亚洲国产成人91porn| 疯狂试爱三2浴室激情视频| 国产视频一区不卡| 亚洲乱码国产乱码精品精大量| 国产成人丝袜美腿| 日本成人xxx| 亚洲三级电影在线观看| 国产aaa免费视频| 欧美国产免费| 国产精品无码乱伦| 欧美好骚综合网| 亚洲国产日韩欧美| 日韩成人影院| 亚洲mv在线看| 热久久天天拍国产| 色综合久久久久久久久五月| 欧美热在线视频精品999| 中文字幕精品一区二区精品| 国产裸体免费无遮挡| 欧美理论在线| 亚洲国产一二三精品无码| 国产精品久久久久久久| 亚洲最大色综合成人av| 日韩久久久久| 一级特黄录像免费播放全99| 日韩国产欧美| 一区二区不卡视频| 99久久综合狠狠综合久久aⅴ| 一本久道久久综合狠狠爱亚洲精品| 精品久久一区| 亚洲欧美日韩另类精品一区二区三区| 最新亚洲精品| 日韩电影大全在线观看| 精品欧美激情在线观看| 日韩福利二区| 婷婷综合五月| 国产免费一区二区三区四在线播放| 欧美肥老太太性生活| 麻豆视频传媒入口| 欧美在线三区| 9久久9毛片又大又硬又粗| 亚洲精一区二区三区| 免费日韩视频在线观看| 日本网站在线观看一区二区三区| 亚洲免费一级视频| 国内久久精品视频| 无码人妻一区二区三区精品视频 | 91影视免费在线观看| 亚洲三级av| 狼狼综合久久久久综合网| 精品日韩欧美一区| 可以免费看的黄色网址| 在线播放不卡| 欧美日韩亚洲一二三| 激情综合网激情| 精品一区二区视频在线观看| 国产日韩精品久久久| 国产精品精品软件男同| 午夜精品国产更新| 久久久999久久久| 日韩欧美激情四射| 黄色在线播放| 免费av一区二区| 中文字幕高清在线播放| 成人激情视频网| 日日狠狠久久偷偷综合色| 亚洲图片在线观看| 99在线精品免费视频九九视| 日本中文字幕高清| 粉嫩av一区二区三区| 欧美18—19性高清hd4k| 亚洲免费av观看| 亚洲熟妇无码乱子av电影| 欧美一级夜夜爽| 国产女人在线视频| 欧美激情综合色| 日韩免费在线电影| 久久久久久99| 国产精品videosex极品| 色综合天天色综合| av一区二区久久| 成人在线观看免费完整| 在线亚洲免费视频| 人人妻人人澡人人爽精品日本| 国产一区二区成人| 国产在线美女| **亚洲第一综合导航网站| 欧美精品一二| 欧美大片在线播放| 国产美女精品一区二区三区| 中文字幕第20页| 婷婷国产v国产偷v亚洲高清| 国产精品视频a| 亚洲性线免费观看视频成熟| 成年网站在线视频网站| 91亚洲精品视频| 久久视频在线| 999精品网站| 91在线观看污| 中文字幕第28页| 91精品国产综合久久久久| 成人免费在线电影| 欧美最近摘花xxxx摘花| 国产精品sss在线观看av| 2021狠狠干| 激情深爱一区二区| 国产精品一区二区亚洲| 色香色香欲天天天影视综合网| 手机看片福利在线| 久久久视频在线| 2020最新国产精品| 九九久久九九久久| 国内精品伊人久久久久影院对白| 欧美福利在线视频| 欧美综合欧美视频| 高清国产福利在线观看| 国产91在线播放精品91| 亚洲美女15p| 日本wwww视频| 久久天天做天天爱综合色| 国产在线观看黄色| 国产丝袜一区二区三区免费视频| 中文字幕第一页在线播放| 亚洲尤物影院| 中文字幕线观看| 中文字幕欧美一| 国产乱淫片视频| 欧美成人精品一区二区| 日韩高清一区| 农民人伦一区二区三区| 9人人澡人人爽人人精品| 日韩毛片在线播放| 日韩精品在线私人| 欧美magnet| 亚洲精品成人久久久998| 另类欧美日韩国产在线| 国产美女福利视频| 欧美videossexotv100| 99热99re6国产在线播放| 精品视频在线观看| 蜜桃久久av| 成年人免费视频播放| 91精品国产欧美一区二区18| 青春草在线视频| 精品一区久久久久久| 久热精品在线| 亚洲激情图片网| 日韩一二在线观看| a'aaa级片在线观看| 日本视频一区二区在线观看| 蜜桃视频一区二区三区| 麻豆明星ai换脸视频| 亚洲韩国欧洲国产日产av| 日日av拍夜夜添久久免费| 高清视频一区| 噜噜噜躁狠狠躁狠狠精品视频| 国产毛片欧美毛片久久久| 欧美一三区三区四区免费在线看 | 欧美一级淫片| 免费国偷自产拍精品视频| 性做久久久久久免费观看| 国产精品一二三区视频| 91在线视频免费| 亚洲一区成人| www欧美com| 亚洲欧美另类中文字幕| 国产成年精品| aa在线免费观看| ...xxx性欧美| 青青草娱乐在线| 91精品国产一区二区三区动漫| 国产精品呻吟| 亚洲最大的黄色网址| 亚洲人成绝费网站色www| 中文字幕一区二区三区中文字幕| 99视频免费播放| 午夜一区二区三区视频| 日产精品久久久一区二区|