婷婷综合国产,91蜜桃婷婷狠狠久久综合9色 ,九九九九九精品,国产综合av

主頁(yè) > 知識(shí)庫(kù) > Go 并發(fā)控制context實(shí)現(xiàn)原理剖析(小結(jié))

Go 并發(fā)控制context實(shí)現(xiàn)原理剖析(小結(jié))

熱門標(biāo)簽:江西轉(zhuǎn)化率高的羿智云外呼系統(tǒng) 高德地圖標(biāo)注口訣 中國(guó)地圖標(biāo)注省會(huì)高清 學(xué)海導(dǎo)航地圖標(biāo)注 地圖標(biāo)注的汽車標(biāo) 西部云谷一期地圖標(biāo)注 南通如皋申請(qǐng)開(kāi)通400電話 廣州呼叫中心外呼系統(tǒng) 浙江高速公路地圖標(biāo)注

1. 前言

Golang context是Golang應(yīng)用開(kāi)發(fā)常用的并發(fā)控制技術(shù),它與WaitGroup最大的不同點(diǎn)是context對(duì)于派生goroutine有更強(qiáng)的控制力,它可以控制多級(jí)的goroutine。

context翻譯成中文是"上下文",即它可以控制一組呈樹狀結(jié)構(gòu)的goroutine,每個(gè)goroutine擁有相同的上下文。

典型的使用場(chǎng)景如下圖所示:

上圖中由于goroutine派生出子goroutine,而子goroutine又繼續(xù)派生新的goroutine,這種情況下使用WaitGroup就不太容易,因?yàn)樽觛oroutine個(gè)數(shù)不容易確定。而使用context就可以很容易實(shí)現(xiàn)。

2. Context實(shí)現(xiàn)原理

context實(shí)際上只定義了接口,凡是實(shí)現(xiàn)該接口的類都可稱為是一種context,官方包中實(shí)現(xiàn)了幾個(gè)常用的context,分別可用于不同的場(chǎng)景。

2.1 接口定義

源碼包中src/context/context.go:Context 定義了該接口:

type Context interface {
  Deadline() (deadline time.Time, ok bool)

  Done() -chan struct{}

  Err() error

  Value(key interface{}) interface{}
}

基礎(chǔ)的context接口只定義了4個(gè)方法,下面分別簡(jiǎn)要說(shuō)明一下:

2.1.1 Deadline()

該方法返回一個(gè)deadline和標(biāo)識(shí)是否已設(shè)置deadline的bool值,如果沒(méi)有設(shè)置deadline,則ok == false,此時(shí)deadline為一個(gè)初始值的time.Time值

2.1.2 Done()

該方法返回一個(gè)channel,需要在select-case語(yǔ)句中使用,如"case -context.Done():"。

當(dāng)context關(guān)閉后,Done()返回一個(gè)被關(guān)閉的管道,關(guān)閉的管理仍然是可讀的,據(jù)此goroutine可以收到關(guān)閉請(qǐng)求;當(dāng)context還未關(guān)閉時(shí),Done()返回nil。

2.1.3 Err()

該方法描述context關(guān)閉的原因。關(guān)閉原因由context實(shí)現(xiàn)控制,不需要用戶設(shè)置。比如Deadline context,關(guān)閉原因可能是因?yàn)閐eadline,也可能提前被主動(dòng)關(guān)閉,那么關(guān)閉原因就會(huì)不同:

  • 因deadline關(guān)閉:“context deadline exceeded”;
  • 因主動(dòng)關(guān)閉: "context canceled"。

當(dāng)context關(guān)閉后,Err()返回context的關(guān)閉原因;當(dāng)context還未關(guān)閉時(shí),Err()返回nil;

2.1.3 Value()

有一種context,它不是用于控制呈樹狀分布的goroutine,而是用于在樹狀分布的goroutine間傳遞信息。

Value()方法就是用于此種類型的context,該方法根據(jù)key值查詢map中的value。具體使用后面示例說(shuō)明。

2.2 空context

context包中定義了一個(gè)空的context, 名為emptyCtx,用于context的根節(jié)點(diǎn),空的context只是簡(jiǎn)單的實(shí)現(xiàn)了Context,本身不包含任何值,僅用于其他context的父節(jié)點(diǎn)。

emptyCtx類型定義如下代碼所示:

type emptyCtx int

func (*emptyCtx) Deadline() (deadline time.Time, ok bool) {
	return
}

func (*emptyCtx) Done() -chan struct{} {
	return nil
}

func (*emptyCtx) Err() error {
	return nil
}

func (*emptyCtx) Value(key interface{}) interface{} {
	return nil
}

context包中定義了一個(gè)公用的emptCtx全局變量,名為background,可以使用context.Background()獲取它,實(shí)現(xiàn)代碼如下所示:

var background = new(emptyCtx)
func Background() Context {
	return background
}

context包提供了4個(gè)方法創(chuàng)建不同類型的context,使用這四個(gè)方法時(shí)如果沒(méi)有父context,都需要傳入backgroud,即backgroud作為其父節(jié)點(diǎn):

  • WithCancel()
  • WithDeadline()
  • WithTimeout()
  • WithValue()

context包中實(shí)現(xiàn)Context接口的struct,除了emptyCtx外,還有cancelCtx、timerCtx和valueCtx三種,正是基于這三種context實(shí)例,實(shí)現(xiàn)了上述4種類型的context。

context包中各context類型之間的關(guān)系,如下圖所示:

struct cancelCtx、valueCtx、valueCtx都繼承于Context,下面分別介紹這三個(gè)struct。

2.3 cancelCtx

源碼包中src/context/context.go:cancelCtx 定義了該類型context:

type cancelCtx struct {
	Context

	mu    sync.Mutex      // protects following fields
	done   chan struct{}     // created lazily, closed by first cancel call
	children map[canceler]struct{} // set to nil by the first cancel call
	err   error         // set to non-nil by the first cancel call
}

children中記錄了由此context派生的所有child,此context被cancle時(shí)會(huì)把其中的所有child都cancle掉。

cancelCtx與deadline和value無(wú)關(guān),所以只需要實(shí)現(xiàn)Done()和Err()接口外露接口即可。

2.3.1 Done()接口實(shí)現(xiàn)

按照Context定義,Done()接口只需要返回一個(gè)channel即可,對(duì)于cancelCtx來(lái)說(shuō)只需要返回成員變量done即可。

這里直接看下源碼,非常簡(jiǎn)單:

func (c *cancelCtx) Done() -chan struct{} {
	c.mu.Lock()
	if c.done == nil {
		c.done = make(chan struct{})
	}
	d := c.done
	c.mu.Unlock()
	return d
}

由于cancelCtx沒(méi)有指定初始化函數(shù),所以cancelCtx.done可能還未分配,所以需要考慮初始化。
cancelCtx.done會(huì)在context被cancel時(shí)關(guān)閉,所以cancelCtx.done的值一般經(jīng)歷如三個(gè)階段:nil --> chan struct{} --> closed chan。

2.3.2 Err()接口實(shí)現(xiàn)

按照Context定義,Err()只需要返回一個(gè)error告知context被關(guān)閉的原因。對(duì)于cancelCtx來(lái)說(shuō)只需要返回成員變量err即可。

還是直接看下源碼:

func (c *cancelCtx) Err() error {
	c.mu.Lock()
	err := c.err
	c.mu.Unlock()
	return err
}

cancelCtx.err默認(rèn)是nil,在context被cancel時(shí)指定一個(gè)error變量: var Canceled = errors.New("context canceled")

2.3.3 cancel()接口實(shí)現(xiàn)

cancel()內(nèi)部方法是理解cancelCtx的最關(guān)鍵的方法,其作用是關(guān)閉自己和其后代,其后代存儲(chǔ)在cancelCtx.children的map中,其中key值即后代對(duì)象,value值并沒(méi)有意義,這里使用map只是為了方便查詢而已。

cancel方法實(shí)現(xiàn)偽代碼如下所示:

func (c *cancelCtx) cancel(removeFromParent bool, err error) {
  c.mu.Lock()
	
  c.err = err	           //設(shè)置一個(gè)error,說(shuō)明關(guān)閉原因
  close(c.done)           //將channel關(guān)閉,以此通知派生的context
	
  for child := range c.children {  //遍歷所有children,逐個(gè)調(diào)用cancel方法
    child.cancel(false, err)
  }
  c.children = nil
  c.mu.Unlock()

  if removeFromParent {      //正常情況下,需要將自己從parent刪除
    removeChild(c.Context, c)
  }
}

實(shí)際上,WithCancel()返回的第二個(gè)用于cancel context的方法正是此cancel()。

2.3.4 WithCancel()方法實(shí)現(xiàn)

WithCancel()方法作了三件事:

  • 初始化一個(gè)cancelCtx實(shí)例
  • 將cancelCtx實(shí)例添加到其父節(jié)點(diǎn)的children中(如果父節(jié)點(diǎn)也可以被cancel的話)
  • 返回cancelCtx實(shí)例和cancel()方法

其實(shí)現(xiàn)源碼如下所示:

func WithCancel(parent Context) (ctx Context, cancel CancelFunc) {
	c := newCancelCtx(parent)
	propagateCancel(parent, c)  //將自身添加到父節(jié)點(diǎn)
	return c, func() { c.cancel(true, Canceled) }
}

這里將自身添加到父節(jié)點(diǎn)的過(guò)程有必要簡(jiǎn)單說(shuō)明一下:

  • 如果父節(jié)點(diǎn)也支持cancel,也就是說(shuō)其父節(jié)點(diǎn)肯定有children成員,那么把新context添加到children里即可;
  • 如果父節(jié)點(diǎn)不支持cancel,就繼續(xù)向上查詢,直到找到一個(gè)支持cancel的節(jié)點(diǎn),把新context添加到children里;
  • 如果所有的父節(jié)點(diǎn)均不支持cancel,則啟動(dòng)一個(gè)協(xié)程等待父節(jié)點(diǎn)結(jié)束,然后再把當(dāng)前context結(jié)束。

2.3.5 典型使用案例

一個(gè)典型的使用cancel context的例子如下所示:

package main

import (
  "fmt"
  "time"
  "context"
)

func HandelRequest(ctx context.Context) {
  go WriteRedis(ctx)
  go WriteDatabase(ctx)
  for {
    select {
    case -ctx.Done():
      fmt.Println("HandelRequest Done.")
      return
    default:
      fmt.Println("HandelRequest running")
      time.Sleep(2 * time.Second)
    }
  }
}

func WriteRedis(ctx context.Context) {
  for {
    select {
    case -ctx.Done():
      fmt.Println("WriteRedis Done.")
      return
    default:
      fmt.Println("WriteRedis running")
      time.Sleep(2 * time.Second)
    }
  }
}

func WriteDatabase(ctx context.Context) {
  for {
    select {
    case -ctx.Done():
      fmt.Println("WriteDatabase Done.")
      return
    default:
      fmt.Println("WriteDatabase running")
      time.Sleep(2 * time.Second)
    }
  }
}

func main() {
  ctx, cancel := context.WithCancel(context.Background())
  go HandelRequest(ctx)

  time.Sleep(5 * time.Second)
  fmt.Println("It's time to stop all sub goroutines!")
  cancel()

  //Just for test whether sub goroutines exit or not
  time.Sleep(5 * time.Second)
}

上面代碼中協(xié)程HandelRequest()用于處理某個(gè)請(qǐng)求,其又會(huì)創(chuàng)建兩個(gè)協(xié)程:WriteRedis()、WriteDatabase(),main協(xié)程創(chuàng)建創(chuàng)建context,并把context在各子協(xié)程間傳遞,main協(xié)程在適當(dāng)?shù)臅r(shí)機(jī)可以cancel掉所有子協(xié)程。

程序輸出如下所示:

HandelRequest running
WriteDatabase running
WriteRedis running
HandelRequest running
WriteDatabase running
WriteRedis running
HandelRequest running
WriteDatabase running
WriteRedis running
It's time to stop all sub goroutines!
WriteDatabase Done.
HandelRequest Done.
WriteRedis Done.

2.4 timerCtx

源碼包中src/context/context.go:timerCtx 定義了該類型context:

type timerCtx struct {
	cancelCtx
	timer *time.Timer // Under cancelCtx.mu.

	deadline time.Time
}

timerCtx在cancelCtx基礎(chǔ)上增加了deadline用于標(biāo)示自動(dòng)cancel的最終時(shí)間,而timer就是一個(gè)觸發(fā)自動(dòng)cancel的定時(shí)器。

由此,衍生出WithDeadline()和WithTimeout()。實(shí)現(xiàn)上這兩種類型實(shí)現(xiàn)原理一樣,只不過(guò)使用語(yǔ)境不一樣:

  • deadline: 指定最后期限,比如context將2018.10.20 00:00:00之時(shí)自動(dòng)結(jié)束
  • timeout: 指定最長(zhǎng)存活時(shí)間,比如context將在30s后結(jié)束。

對(duì)于接口來(lái)說(shuō),timerCtx在cancelCtx基礎(chǔ)上還需要實(shí)現(xiàn)Deadline()和cancel()方法,其中cancel()方法是重寫的。

2.4.1 Deadline()接口實(shí)現(xiàn)

Deadline()方法僅僅是返回timerCtx.deadline而矣。而timerCtx.deadline是WithDeadline()或WithTimeout()方法設(shè)置的。

2.4.2 cancel()接口實(shí)現(xiàn)

cancel()方法基本繼承cancelCtx,只需要額外把timer關(guān)閉。

timerCtx被關(guān)閉后,timerCtx.cancelCtx.err將會(huì)存儲(chǔ)關(guān)閉原因:

  • 如果deadline到來(lái)之前手動(dòng)關(guān)閉,則關(guān)閉原因與cancelCtx顯示一致;
  • 如果deadline到來(lái)時(shí)自動(dòng)關(guān)閉,則原因?yàn)椋?context deadline exceeded"

2.4.3 WithDeadline()方法實(shí)現(xiàn)

WithDeadline()方法實(shí)現(xiàn)步驟如下:

  • 初始化一個(gè)timerCtx實(shí)例
  • 將timerCtx實(shí)例添加到其父節(jié)點(diǎn)的children中(如果父節(jié)點(diǎn)也可以被cancel的話)
  • 啟動(dòng)定時(shí)器,定時(shí)器到期后會(huì)自動(dòng)cancel本context
  • 返回timerCtx實(shí)例和cancel()方法

也就是說(shuō),timerCtx類型的context不僅支持手動(dòng)cancel,也會(huì)在定時(shí)器到來(lái)后自動(dòng)cancel。

2.4.4 WithTimeout()方法實(shí)現(xiàn)

WithTimeout()實(shí)際調(diào)用了WithDeadline,二者實(shí)現(xiàn)原理一致。

看代碼會(huì)非常清晰:

func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc) {
	return WithDeadline(parent, time.Now().Add(timeout))
}

2.4.5 典型使用案例

下面例子中使用WithTimeout()獲得一個(gè)context并在其了協(xié)程中傳遞:

package main

import (
  "fmt"
  "time"
  "context"
)

func HandelRequest(ctx context.Context) {
  go WriteRedis(ctx)
  go WriteDatabase(ctx)
  for {
    select {
    case -ctx.Done():
      fmt.Println("HandelRequest Done.")
      return
    default:
      fmt.Println("HandelRequest running")
      time.Sleep(2 * time.Second)
    }
  }
}

func WriteRedis(ctx context.Context) {
  for {
    select {
    case -ctx.Done():
      fmt.Println("WriteRedis Done.")
      return
    default:
      fmt.Println("WriteRedis running")
      time.Sleep(2 * time.Second)
    }
  }
}

func WriteDatabase(ctx context.Context) {
  for {
    select {
    case -ctx.Done():
      fmt.Println("WriteDatabase Done.")
      return
    default:
      fmt.Println("WriteDatabase running")
      time.Sleep(2 * time.Second)
    }
  }
}

func main() {
  ctx, _ := context.WithTimeout(context.Background(), 5 * time.Second)
  go HandelRequest(ctx)

  time.Sleep(10 * time.Second)
}

主協(xié)程中創(chuàng)建一個(gè)10s超時(shí)的context,并將其傳遞給子協(xié)程,10s自動(dòng)關(guān)閉context。程序輸出如下:

HandelRequest running
WriteRedis running
WriteDatabase running
HandelRequest running
WriteRedis running
WriteDatabase running
HandelRequest running
WriteRedis running
WriteDatabase running
HandelRequest Done.
WriteDatabase Done.
WriteRedis Done.

2.5 valueCtx

源碼包中src/context/context.go:valueCtx 定義了該類型context:

type valueCtx struct {
	Context
	key, val interface{}
}

valueCtx只是在Context基礎(chǔ)上增加了一個(gè)key-value對(duì),用于在各級(jí)協(xié)程間傳遞一些數(shù)據(jù)。

由于valueCtx既不需要cancel,也不需要deadline,那么只需要實(shí)現(xiàn)Value()接口即可。

2.5.1 Value()接口實(shí)現(xiàn)

由valueCtx數(shù)據(jù)結(jié)構(gòu)定義可見(jiàn),valueCtx.key和valueCtx.val分別代表其key和value值。 實(shí)現(xiàn)也很簡(jiǎn)單:

func (c *valueCtx) Value(key interface{}) interface{} {
	if c.key == key {
		return c.val
	}
	return c.Context.Value(key)
}

這里有個(gè)細(xì)節(jié)需要關(guān)注一下,即當(dāng)前context查找不到key時(shí),會(huì)向父節(jié)點(diǎn)查找,如果查詢不到則最終返回interface{}。也就是說(shuō),可以通過(guò)子context查詢到父的value值。

2.5.2 WithValue()方法實(shí)現(xiàn)

WithValue()實(shí)現(xiàn)也是非常的簡(jiǎn)單, 偽代碼如下:

func WithValue(parent Context, key, val interface{}) Context {
	if key == nil {
		panic("nil key")
	}
	return valueCtx{parent, key, val}
}

2.5.3 典型使用案例

下面示例程序展示valueCtx的用法:

package main

import (
  "fmt"
  "time"
  "context"
)

func HandelRequest(ctx context.Context) {
  for {
    select {
    case -ctx.Done():
      fmt.Println("HandelRequest Done.")
      return
    default:
      fmt.Println("HandelRequest running, parameter: ", ctx.Value("parameter"))
      time.Sleep(2 * time.Second)
    }
  }
}

func main() {
  ctx := context.WithValue(context.Background(), "parameter", "1")
  go HandelRequest(ctx)

  time.Sleep(10 * time.Second)
}

上例main()中通過(guò)WithValue()方法獲得一個(gè)context,需要指定一個(gè)父context、key和value。然后通將該context傳遞給子協(xié)程HandelRequest,子協(xié)程可以讀取到context的key-value。

注意:本例中子協(xié)程無(wú)法自動(dòng)結(jié)束,因?yàn)閏ontext是不支持cancle的,也就是說(shuō)-ctx.Done()永遠(yuǎn)無(wú)法返回。如果需要返回,需要在創(chuàng)建context時(shí)指定一個(gè)可以cancel的context作為父節(jié)點(diǎn),使用父節(jié)點(diǎn)的cancel()在適當(dāng)?shù)臅r(shí)機(jī)結(jié)束整個(gè)context。

總結(jié)

Context僅僅是一個(gè)接口定義,跟據(jù)實(shí)現(xiàn)的不同,可以衍生出不同的context類型;

cancelCtx實(shí)現(xiàn)了Context接口,通過(guò)WithCancel()創(chuàng)建cancelCtx實(shí)例;

timerCtx實(shí)現(xiàn)了Context接口,通過(guò)WithDeadline()和WithTimeout()創(chuàng)建timerCtx實(shí)例;

valueCtx實(shí)現(xiàn)了Context接口,通過(guò)WithValue()創(chuàng)建valueCtx實(shí)例;

三種context實(shí)例可互為父節(jié)點(diǎn),從而可以組合成不同的應(yīng)用形式;

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

您可能感興趣的文章:
  • Go并發(fā)控制WaitGroup的使用場(chǎng)景分析
  • Golang 實(shí)現(xiàn)分片讀取http超大文件流和并發(fā)控制
  • Django Channels 實(shí)現(xiàn)點(diǎn)對(duì)點(diǎn)實(shí)時(shí)聊天和消息推送功能
  • 基于django channel實(shí)現(xiàn)websocket的聊天室的方法示例
  • Go并發(fā)控制Channel使用場(chǎng)景分析

標(biāo)簽:曲靖 保定 常州 東營(yíng) 許昌 吐魯番 貴州 德宏

巨人網(wǎng)絡(luò)通訊聲明:本文標(biāo)題《Go 并發(fā)控制context實(shí)現(xiàn)原理剖析(小結(jié))》,本文關(guān)鍵詞  并發(fā),控制,context,實(shí)現(xiàn),原理,;如發(fā)現(xiàn)本文內(nèi)容存在版權(quán)問(wèn)題,煩請(qǐng)?zhí)峁┫嚓P(guān)信息告之我們,我們將及時(shí)溝通與處理。本站內(nèi)容系統(tǒng)采集于網(wǎng)絡(luò),涉及言論、版權(quán)與本站無(wú)關(guān)。
  • 相關(guān)文章
  • 下面列出與本文章《Go 并發(fā)控制context實(shí)現(xiàn)原理剖析(小結(jié))》相關(guān)的同類信息!
  • 本頁(yè)收集關(guān)于Go 并發(fā)控制context實(shí)現(xiàn)原理剖析(小結(jié))的相關(guān)信息資訊供網(wǎng)民參考!
  • 推薦文章
    婷婷综合国产,91蜜桃婷婷狠狠久久综合9色 ,九九九九九精品,国产综合av
    日韩欧美激情四射| 国产精品国产三级国产aⅴ入口| 国产91色综合久久免费分享| 国产在线播放一区| 国产aⅴ综合色| 91美女在线视频| 色8久久精品久久久久久蜜| 精品视频在线免费观看| 精品剧情v国产在线观看在线| 精品国产伦一区二区三区观看体验 | 337p粉嫩大胆色噜噜噜噜亚洲 | 亚洲男女一区二区三区| 五月婷婷另类国产| 国产精品一二二区| 91国偷自产一区二区开放时间 | 欧美久久久久中文字幕| 久久一区二区三区四区| 亚洲免费观看高清完整版在线观看熊 | 日韩福利视频网| 九九九久久久精品| 91丨porny丨户外露出| 欧美一级日韩不卡播放免费| 国产精品看片你懂得| 人人超碰91尤物精品国产| 成人午夜av电影| 欧美mv日韩mv国产网站app| 亚洲日韩欧美一区二区在线| 免费成人在线视频观看| 91丨九色丨黑人外教| 久久久国产午夜精品 | 91久久香蕉国产日韩欧美9色| 精品欧美一区二区三区精品久久| 亚洲欧美另类久久久精品| 精东粉嫩av免费一区二区三区| 91伊人久久大香线蕉| 精品国产乱子伦一区| 亚洲一区影音先锋| 国产精品影视网| 4438x亚洲最大成人网| 亚洲精品菠萝久久久久久久| 国产999精品久久久久久绿帽| 91精品国产综合久久香蕉麻豆| 亚洲私人影院在线观看| 国产精品99久久久久久久女警| 欧美精品丝袜久久久中文字幕| 亚洲精品美腿丝袜| 99精品一区二区三区| 国产三级一区二区| 国产精品香蕉一区二区三区| 5566中文字幕一区二区电影| 亚洲va欧美va国产va天堂影院| 91国模大尺度私拍在线视频| 中文字幕一区免费在线观看 | 91精品国产91久久久久久最新毛片| 亚洲人成人一区二区在线观看| 风间由美一区二区av101| 精品福利一二区| 极品瑜伽女神91| 欧美mv日韩mv国产| 久久99国产精品久久99| 欧美二区三区的天堂| 亚洲国产美国国产综合一区二区| 91污片在线观看| 亚洲特黄一级片| 色哟哟精品一区| 亚洲精品少妇30p| 欧美中文字幕一二三区视频| 一区二区在线免费观看| 91免费观看视频| 亚洲精品免费看| 欧美性三三影院| 五月激情丁香一区二区三区| 欧美一区二区三区成人| 伦理电影国产精品| 久久久.com| 成人一区二区三区视频在线观看| 国产精品久久毛片av大全日韩| 99久久综合精品| 亚洲精品国产a| 制服丝袜亚洲色图| 国产麻豆一精品一av一免费| 国产精品免费视频一区| 色一情一乱一乱一91av| 亚洲自拍偷拍图区| 日韩欧美卡一卡二| 成人污视频在线观看| 亚洲图片欧美色图| 日韩午夜在线观看| 成人免费观看av| 亚洲综合久久久久| 精品国产a毛片| 色综合天天综合网天天狠天天| 午夜精品123| 国产视频在线观看一区二区三区 | 欧美在线一区二区| 蜜桃一区二区三区在线| 中文字幕高清不卡| 在线精品视频一区二区| 日本三级韩国三级欧美三级| 日韩美女视频一区| 日韩一区二区三区免费看 | 国产一区二区三区四区五区入口| 国产精品视频yy9299一区| 欧美亚洲一区二区在线观看| 麻豆精品一区二区三区| 中文字幕亚洲一区二区av在线 | 免费高清在线一区| 国产精品萝li| 宅男在线国产精品| 99久久久国产精品免费蜜臀| 激情综合五月婷婷| 亚洲已满18点击进入久久| 精品日韩99亚洲| 欧美揉bbbbb揉bbbbb| 成人成人成人在线视频| 九九国产精品视频| 天天影视网天天综合色在线播放| 国产精品一二三四区| 亚洲国产aⅴ成人精品无吗| 国产精品欧美一区二区三区| 538prom精品视频线放| 色婷婷综合久久久久中文一区二区| 国产精品一区二区在线观看不卡 | 亚洲精品久久7777| 中文字幕不卡在线观看| 精品国产一区二区三区不卡| 欧美美女一区二区三区| 欧美在线观看视频在线| 成人黄色在线视频| 韩国女主播一区| 免费视频一区二区| 亚洲成人www| 亚洲精品久久7777| 最新中文字幕一区二区三区| 久久精品欧美一区二区三区不卡| 91精品国产一区二区三区香蕉| 欧美中文字幕一区二区三区亚洲| 99精品国产99久久久久久白柏| 国产精品乡下勾搭老头1| 久久精品国产久精国产| 免费在线一区观看| 奇米影视7777精品一区二区| 亚洲午夜在线视频| 亚洲成人综合视频| 亚洲成人精品一区二区| 亚洲图片自拍偷拍| 日一区二区三区| 日韩福利电影在线观看| 蜜臀久久99精品久久久画质超高清 | 欧美精品黑人性xxxx| 欧美日韩高清一区| 91麻豆精品国产91久久久资源速度| 欧美一区二区三区视频在线| 日韩欧美不卡在线观看视频| 精品国产一二三| 国产精品三级av在线播放| 国产精品久久久久久亚洲伦| 中文字幕一区av| 亚洲综合丁香婷婷六月香| 污片在线观看一区二区| 亚洲成人av资源| 视频在线在亚洲| 另类小说视频一区二区| 国产精品资源网站| 成人avav影音| 欧美色综合影院| 日韩欧美一区在线| 国产校园另类小说区| 亚洲精选视频在线| 免费在线观看不卡| 成人午夜碰碰视频| 在线观看亚洲成人| 欧美一区二区在线不卡| 欧美精品一区二区三区很污很色的 | 久久精品日韩一区二区三区| 国产精品国产自产拍在线| 一区二区三区中文在线观看| 日韩成人午夜电影| 国产.欧美.日韩| 欧美主播一区二区三区| wwww国产精品欧美| 亚洲日本欧美天堂| 丝袜美腿成人在线| 成人免费视频国产在线观看| 在线国产电影不卡| 国产网站一区二区三区| 亚洲五码中文字幕| 粉嫩一区二区三区在线看| 在线精品视频一区二区| 久久色.com| 亚洲成国产人片在线观看| 国产精品一区二区果冻传媒| 欧美性大战久久| 久久久久久一级片| 日韩高清在线电影| av亚洲精华国产精华精华| 777xxx欧美| 亚洲精品乱码久久久久久日本蜜臀| 精品亚洲国产成人av制服丝袜| 色呦呦日韩精品|