青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

每天早晨叫醒你的不是鬧鐘,而是夢想

  C++博客 :: 首頁 :: 聯系 :: 聚合  :: 管理
  62 Posts :: 0 Stories :: 5 Comments :: 0 Trackbacks

常用鏈接

留言簿(1)

我參與的團隊

搜索

  •  

最新評論

閱讀排行榜

評論排行榜

http://www.c-view.org/journal/006/gp_aa.htm 


并不是我故意想弄糟你的心情,但是在這期專欄里,我們將討論多線程編程這一話題。正如上一期Generic里所說的,編寫異常安全(exception-safe)的程序是非常困難的,但是和編寫多線程程序比起來,那簡直就是兒戲。  

多線程的程序是出了名的難編寫、難驗證、難調試、難維護,這通常是件苦差事。不正確的多線程程序可能可以運行很多年也不出一點錯,直到滿足某些臨界的條件時,才出現意想不到的奇怪錯誤。  

不用說,編寫多線程程序的程序員需要使用可能得到的所有幫助。這期專欄將專注于討論競爭條件(race conditions)——這通常是多線程程序中各種麻煩的根源——深入了解它并提供一些工具來防止競爭。令人驚異的是,我們將讓編譯器盡其所能來幫助你做這些事。

僅僅一個不起眼的關鍵字 
盡管C和C++標準對于線程都明顯的“保持沉默”,但它們以volatile關鍵字的形式,確實為多線程保留了一點特權。  

就象大家更熟悉的const一樣,volatile是一個類型修飾符(type modifier)。它是被設計用來修飾被不同線程訪問和修改的變量。如果沒有volatile,基本上會導致這樣的結果:要么無法編寫多線程程序,要么編譯器失去大量優化的機會。下面我們來一個個說明。  

考慮下面的代碼:  

 1class Gadget
 2{
 3public:
 4    void Wait()
 5    {
 6        while (!flag_)
 7        {
 8            Sleep(1000); // sleeps for 1000 milliseconds
 9        }

10    }

11    void Wakeup()
12    
13        flag_ = true
14    }

15    
16private
17    bool flag_; 
18}
;
19
20


 
上面代碼中Gadget::Wait的目的是每過一秒鐘去檢查一下flag_成員變量,當flag_被另一個線程設為true時,該函數才會返回。至少這是程序作
者的意圖,然而,這個Wait函數是錯誤的.假設編譯器發現Sleep(1000)是調用一個外部的庫函數,它不會改變成員變量flag_,那么編譯器就可
以斷定它可以把flag_緩存在寄存器中,以后可以訪問該寄存器來代替訪問較慢的主板上的內存。這對于單線程代碼來說是一個很好的優化,但
是在現在這種情況下,它破壞了程序的正確性:當你調用了某個Gadget的Wait函數后,即使另一個線程調用了Wakeup,Wait還是會一直循環下去。
這是因為flag_的改變沒有反映到緩存它的寄存器中去。編譯器的優化未免有點太……樂觀了。

在大多數情況下,把變量緩存在寄存器中是一個非常有價值的優化方法,如果不用的話很可惜。C和C++給你提供了顯式禁用這種緩存優化的機會。
如果你聲明變量是使用了volatile修飾符,編譯器就不會把這個變量緩存在寄存器里——每次訪問都將去存取變量在內存中的實際位置。這樣你
要對Gadget的Wait/Wakeup做的修改就是給flag_加上正確的修飾:  

class Gadget 
{ 
public: 
    ... as above ... 
private: 
    volatile bool flag_; 
}; 



大多數關于volatile的原理和用法的解釋就到此為止,并且建議你用volatile修飾在多個線程中使用的原生類型變量。然而,你可以用volatile
做更多的事,因為它是神奇的C++類型系統的一部分。




把volatile用于自定義類型 
volatile修飾不僅可以用于原生類型,也可以用于自定義類型。這時候,volatile修飾方式類似于const(你也可以對一個類型同時使用const
和volatile).

與const不同,volatile的作用對于原生類型和自定義類型是有區別的。就是說,原生類型有volatile修飾時,仍然支持它們的各種操作(加、
乘、賦值等等),然而對于class來說,就不是這樣。舉例來說,你可以把一個非volatile的int的值賦給一個volatile的int,但是你不能把一
個非volatile的對象賦給一個volatile對象。 

讓我們舉個例子來說明自定義類型的volatile是怎么工作的。  
class Gadget

{
public:
    void Foo() volatile;
    void Bar();
    ...
private:
    String name_;
    int state_;
};
...

Gadget regularGadget;
volatile Gadget volatileGadget;



如果你認為volatile對于對象來說沒有什么作用的話,那你可要大吃一驚了。  
volatileGadget.Foo(); // ok, volatile fun called for 
                          // volatile object 
regularGadget.Foo();  // ok, volatile fun called for 
                          // non-volatile object 
volatileGadget.Bar(); // error! Non-volatile function called for 
                          // volatile object! 

從沒有volatile修飾的類型到相應的volatile類型的轉換是很平常的。但是,就象const一樣,你不能反過來把volatile類型轉換為非volatile類型。你必須用類型轉換運算符:  
Gadget& ref = const_cast<Gadget&>;(volatileGadget); 
ref.Bar(); // ok 

一個有volatile修飾的類只允許訪問其接口的一個子集,這個子集由類的實現者來控制。用戶只有用const_cast才可以訪問這個類型的全部接口。而且,象const一樣,類的volatile屬性會傳遞給它的成員(例如,volatileGadget.name_和volatileGadget.state_也是volatile變量)。

volatile,臨界區和競爭條件 
多線程程序中最簡單也是最常用的同步機制要算是mutex(互斥對象)了。一個mutex只提供兩個基本操作:Acquire和Release。一旦某個線程調用了Acquire,其他線程再調用Acquire時就會被阻塞。當這個線程調用Release后,剛才阻塞在Acquire里的線程中,會有一個且僅有一個被喚醒。換句話說,對于一個給定的mutex,只有一個線程可以在Acquire和Release調用之間獲取處理器時間。在Acquire和Release調用之間執行的代碼叫做臨界區(critical section)。(Windows的用語可能會引起一點混亂,因為Windows把mutex本身叫做臨界區,而Windows的mutex實際上指進程間的mutex。如果把它們分別叫作線程mutex和進程mutex可能會好些。)  

Mutex是用來避免數據出現競爭條件。根據定義,所謂競爭條件就是這樣一種情況:多個線程對數據產生的作用要依賴于線程的調度順序的。當兩個線程競相訪問同一數據時,就會發生競爭條件。因為一個線程可以在任意一個時刻打斷其他線程,數據可能會被破壞或者被錯誤地解釋。因此,對數據的修改操作,以及有些情況下的訪問操作,必須用臨界區保護起來。在面向對象的編程中,這通常意味著你在一個類的成員變量中保存一個mutex,然后在你訪問這個類的狀態時使用這個mutex。  

多線程編程高手看了上面兩個段落,可能已經在打哈欠了,但是它們的目的只是提供一個準備練習,我們現在要和volatile聯系起來了。我們將把C++的類型和線程的語義作一個對比。

在一個臨界區以外,任意線程會在任何時間打斷別的線程;這是不受控制的,所以被多個線程訪問的變量容易被改得面目全非。這和volatile的原意[1]是一致的——所以需要用volatile來防止編譯器無意地緩存這樣的變量。  

在由一個mutex限定的臨界區里,只有一個線程可以進入。因此,在臨界區中執行的代碼有和單線程程序有相同的語義。被控制的變量不會再被意外改變——你可以去掉volatile修飾。  

簡而言之,線程間共享的數據在臨界區之外是volatile的,而在臨界區之內則不是。  

你通過對一個mutex加鎖來進入一個臨界區,然后你用const_cast去掉某個類型的volatile修飾,如果我們能成功地把這兩個操作放到一起,那么我們就在C++類型系統和應用程序的線程語義建立起聯系。這樣我們可以讓編譯器來幫我們檢測競爭條件。

LockingPtr 
我們需要有一個工具來做mutex的獲取和const_cast兩個操作。讓我們來設計一個LockingPtr類,你需要用一個volatile的對象obj和一個mutex對象mtx來初始化它。在LockingPtr對象的生命期中,它會保證mtx處于被獲取狀態,而且也提供對去掉volatile修飾的obj的訪問。對obj的訪問類似于smart pointer,是通過operator->;和operator*來進行的。const_cast是在LockingPtr內部進行。這個轉化在語義上是正確的,因為LockingPtr在其生存期中始終擁有mutex。  

首先,我們來定義和LockingPtr一起工作的Mutex類的框架:  

class Mutex

{

public:

    void Acquire();

    void Release();

    ...    

};


為了使用LockingPtr,你需要用操作系統提供的數據結構和底層函數來實現Mutex。  
LockingPtr是一個模板,用被控制變量的類型作為模板參數。例如,如果你希望控制一個Widget,你就要這樣寫LockingPtr <Widget>;。  

LockingPtr的定義很簡單,它只是實現了一個單純的smart pointer。它關注的焦點只是在于把const_cast和臨界區操作放在一起。  

template <typename T>;

class LockingPtr 
{

public:

   // Constructors/destructors

   LockingPtr(volatile T& obj, Mutex& mtx)

       : pObj_(const_cast<T*>;(&obj)),

        pMtx_(&mtx)

   {    mtx.Lock();    }

   ~LockingPtr()

   {    pMtx_->;Unlock();    }

   // Pointer behavior

   T& operator*()

   {    return *pObj_;    }

   T* operator->;()

   {   return pObj_;   }

private:
   T* pObj_;

   Mutex* pMtx_;

   LockingPtr(const LockingPtr&);

   LockingPtr& operator=(const LockingPtr&);

};

盡管很簡單,LockingPtr對于編寫正確的多線程代碼非常有用。你應該把線程間共享的對象聲明為volatile,但是永遠不要對它們使用
const_cast——你應該始終是用LockingPtr的自動對象(automatic objects)。讓我們舉例來說明。

比如說你有兩個線程需要共享一個vector<char>;對象: 

class SyncBuf {

public:
    void Thread1();
    void Thread2();
private:
    typedef vector<char>; BufT;
    volatile BufT buffer_;
    Mutex mtx_; // controls access to buffer_
};

在一個線程的函數里,你只需要簡單地使用一個LockingPtr<BufT>;對象來獲取對buffer_成員變量的受控訪問:

void SyncBuf::Thread1() {

    LockingPtr<BufT>; lpBuf(buffer_, mtx_);
    BufT::iterator i = lpBuf->;begin();
    for (; i != lpBuf->;end(); ++i) {
        ... use *i ...
    }
}

這樣的代碼很容易編寫,也很容易理解——每當你需要使用buffer_時,你必須創建一個LockingPtr<BufT>;來指向它。當你這樣做了以后,你就
可以訪問vector的全部接口。

這個方法的好處是,如果你犯了錯誤,編譯器會指出它:  


void SyncBuf::Thread2() {
    // Error! Cannot access 'begin' for a volatile object
    BufT::iterator i = buffer_.begin();
    // Error! Cannot access 'end' for a volatile object
    for (; i != lpBuf->;end(); ++i) {
        ... use *i ...
    }
}

你不能訪問buffer_的任何函數,除非你進行了const_cast或者用LockingPtr。這兩者的區別是LockingPtr提供了一個有規則的方法來對一個volatile變量進行const_cast。  
LockingPtr有非常好的表達力。如果你只需要調用一個函數,你可以創建一個無名的臨時LockingPtr對象,然后直接使用它:  

unsigned int SyncBuf::Size() {

    return LockingPtr<BufT>;(buffer_, mtx_)->;size();

}


回到原生類型 
我們已經看到了volatile對于保護對象免于不受控的訪問是多么出色,并且看到了LockingPtr是怎么提供了一個簡單有效的辦法來編寫線程安全的代碼。現在讓我們回到原生類型,volatile對它們的作用方式是不同的。 

讓我們來考慮一個多個線程共享一個int變量的例子。  


class Counter
{
public:
    ...
    void Increment() { ++ctr_; }
    void Decrement() { --ctr_; }
private:
    int ctr_;
};


如果Increment和Decrement是在不同的線程里被調用的,上面的代碼片斷里就有bug。首先,ctr_必須是volatile的。其次,即使是一個看上去是原子的操作,比如++ctr_,實際上也分為三個階段。內存本身是沒有運算功能的,當對一個變量進行增量操作時,處理器會:  
把變量讀入寄存器  
對寄存器里的值加1  
把結果寫回內存  
這個三步操作稱為RMW(Read-Modify-Write)。在一個RMW操作的Modify階段,大多數處理器都會釋放內存總線,以使其他處理器能夠訪問內存。  
如果在這個時候另一個處理器對同一個變量也進行RMW操作,我們就遇到了一個競爭條件:第二次寫入會覆蓋掉第一次的值。  

為了防止這樣的事發生,你又要用到LockingPtr:  

class Counter

{
public:
    ...
    void Increment() { ++*LockingPtr<int>;(ctr_, mtx_); }
    void Decrement() { --*LockingPtr<int>;(ctr_, mtx_); }
private:
    volatile int ctr_;
    Mutex mtx_;
};

現在這段代碼正確了,但是和SyncBuf相比,這段代碼的質量要差一些。為什么?因為對于Counter,編譯器不會在你錯誤地直接訪問ctr_(沒有對它加鎖)時產生警告。雖然ctr_是volatile的,但是編譯器還是可以編譯++ctr_,盡管產生的代碼絕對是不正確的。編譯器不再是你的盟友了,你只有自己留意競爭條件。  
那么你該怎么做呢?很簡單,你可以用一個高層的結構來包裝原生類型的數據,然后對那個結構使用volatile。這有點自相矛盾,直接用volatile修飾原生類型是一個不好的用法,盡管這是volatile最初期望的用法!
到現在為止,我們討論了具有volatile數據成員的類;現在讓我們來考慮設計這樣的類,它會作為更大的對象的一部分并且在線程間共享。這里,volatile的成員函數可以幫很大的忙。  

在設計類的時候,你只對那些線程安全的成員函數加volatile修飾。你必須假定外面的代碼會在任何地方任何時間調用volatile成員函數。不要忘記:volatile相當于自由的多線程代碼,并且沒有臨界區;非volatile相當于單線程的環境或者在臨界區內。  

比如說,你定義了一個Widget類,它用兩個方法實現了同一個操作——一個線程安全的方法和一個快速的不受保護的方法。

class Widget

{
public:
    void Operation() volatile;
    void Operation();
    ...
private:
    Mutex mtx_;
};


注意這里的重載(overloading)用法。現在Widget的用戶可以用一致的語法調用Operation,對于volatile對象可以得到線程安全性,對于普通對象可以得到速度。用戶必須注意把共享的Widget對象定義為volatile。  
在實現volatile成員函數時,第一個操作通常是用LockingPtr對this進行加鎖,然后其余工作可以交給非volatile的同名函數做: 

void Widget::Operation() volatile

{
    LockingPtr<Widget>; lpThis(*this, mtx_);
    lpThis->;Operation(); // invokes the non-volatile function
}

小結 
在編寫對線程程序的時候,使用volatile將對你十分有益。你必須堅持下面的規則:  

把所有共享對象聲明為volatile  
不要對原生類型直接使用volatile  
定義共享類時,用volatile成員函數來表示它的線程安全性。  
如果你這么做了,而且用了簡單的通用組件LockingPtr,你就可以寫出線程安全的代碼,并且大大減少對競爭條件的擔心,因為編譯器會替你操心,并且勤勤懇懇地為你指出哪里錯了。  

在我參與的幾個項目中,使用volatile和LockingPtr產生了很大效果。代碼十分整潔,也容易理解。我記得遇到過一些死鎖的情況,但是相對于競爭條件,我寧愿對付死鎖的情況,因為它們調試起來容易多了。那些項目實際上根本沒有碰到過有關競爭條件的問題。  

致謝 
非常感謝James Kanze和Sorin Jianu提供了很有洞察力的意見。
致謝 
非常感謝James Kanze和Sorin Jianu提供了很有洞察力的意見。  

附:濫用volatile的本質?[2] 
在上一期的專欄《Generic<Programming>;: volatile — Multithreaded Programmer's Best Friend》發表以后,我收到很多反饋意見。就像是注定的一樣,大部分稱贊都是私人信件,而抱怨都發到USENET新聞組comp.lang.c++.moderated和 comp.programming.threads里去了。隨后引起了很長很激烈的討論,如果你對這個主題有興趣,你可以去看看這個討論,它的標題是“volatile, was: memory visibility between threads.”。  

我知道我從這個討論中學到了很多東西。比如說,文章開頭的Widget的例子不太切題。長話短說,在很多系統(比如POSIX兼容的系統)中,volatile修飾是不需要的,而在另一些系統中,即使加了volatile也沒有用,程序還是不正確。  

關于volatile correctness,最重要的一個問題是它依賴于類似POSIX的mutex,如果在多處理器系統上,光靠mutex就不夠了——你必須用memory barriers。  

另一個更哲理性的問題是:嚴格來說通過類型轉換把變量的volatile屬性去掉是不合法的,即使volatile屬性是你自己為了volatile correctness而加上去的。正如Anthony Williams指出的,可以想象一個系統可能把volatile數據放在一個不同于非volatile數據的存儲區中,在這種情況下,進行地址變換會有不確定的行為。 

另一個批評是volatile correctness雖然可以在一個較低層次上解決競爭條件,但是不能正確的檢測出高層的、邏輯的競爭條件。例如,你有一個mt_vector模版類,用來模擬std::vector,成員函數經過正確的線程同步修正。考慮這段代碼:  

volatile mt_vector<int>; vec; 
 
if (!vec.empty()) { 
    vec.pop_back(); 
} 

這段代碼的目的是刪除vector里的最后一個元素,如果它存在的話。在單線程環境里,他工作地很好。然而如果你把它用在多線程程序里,這段代碼還是有可能拋出異常,盡管empty和pop_back都有正確的線程同步行為。雖然底層的數據(vec)的一致性有保證,但是高層操作的結果還是不確定的。  
無論如何,經過辯論之后,我還是保持我的建議,在有類POSIX的mutex的系統上,volatile correctness還是檢測競爭條件的一個有價值的工具。但是如果你在一個支持內存訪問重新排序的多處理器系統上,你首先需要仔細閱讀你的編譯器的文檔。你必須知己知彼。
最后,Kenneth Chiu提到了一篇非常有趣的文章http://theory.stanford.edu/~freunds/race.ps,猜猜題目是什么?“Type-Based Race Detection for Java”。這篇文章講了怎么對Java的類型系統作一點小小的補充,從而讓編譯器和程序員一起在編譯時檢測競爭條件。  

Eppur si muove.[3] 
譯注 
[1] Volatile原意為易變的,反復無常的。  

[2] 這是Andrei在他的下一篇Generic<Programming>;里對本文作的補充說明。  

[3] 這是伽利略在被迫放棄他一直信仰的哥白尼的地動說時所說過的一句辯解的話,意思是“它(地球)畢竟仍在運動”。

posted on 2011-04-05 17:41 沛沛 閱讀(453) 評論(0)  編輯 收藏 引用 所屬分類: C++
青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            欧美激情导航| 欧美韩日亚洲| 欧美国产视频一区二区| 一区二区三区精品| 亚洲精品一二区| 亚洲精品资源美女情侣酒店| …久久精品99久久香蕉国产| 国产伪娘ts一区| 欧美连裤袜在线视频| 欧美激情二区三区| 欧美婷婷久久| 国产一区在线播放| 亚洲国产三级| 亚洲欧美日韩久久精品 | 在线视频精品一| 一本久久综合亚洲鲁鲁五月天 | 国产精品自拍网站| 亚洲国产精品福利| 亚洲毛片av在线| 亚洲午夜未删减在线观看| 亚洲欧美日韩精品久久奇米色影视 | 午夜国产精品视频免费体验区| 亚洲欧美日韩在线一区| 欧美一级在线播放| 亚洲欧洲一区二区三区在线观看| 免费久久久一本精品久久区| 亚洲高清电影| 久久免费黄色| 国产精品日韩二区| 这里只有精品视频| 欧美国产在线观看| 久久久久久久久久久成人| 欧美小视频在线观看| 亚洲区中文字幕| 亚洲国产精品999| 美女日韩欧美| 亚洲视频专区在线| 亚洲国产专区| 一区二区三区我不卡| 亚洲欧美一区在线| 99精品久久久| 国产精品久久国产精麻豆99网站| 亚洲精品资源美女情侣酒店| 欧美激情精品久久久久久久变态 | 9l视频自拍蝌蚪9l视频成人| 久久综合电影| 国产精品亚洲美女av网站| 亚洲免费在线观看| 亚洲一区在线播放| 国内精品久久久久影院 日本资源 国内精品久久久久伊人av | 一本色道综合亚洲| 日韩一二三在线视频播| 国产精品久久久久久妇女6080| 夜夜爽av福利精品导航 | 亚洲欧美国产77777| 一区二区三区欧美| 国内久久婷婷综合| 亚洲国产精品v| 国产精品久久激情| 蜜桃视频一区| 欧美午夜精品电影| 亚洲国产精品久久久久秋霞影院| 欧美精品18| 久久免费高清视频| 欧美日韩中文| 亚洲国产精品久久久久婷婷884| 国产精品女同互慰在线看| 欧美激情第五页| 国产一区二区成人久久免费影院| 亚洲国产高清一区二区三区| 精品av久久久久电影| 性欧美1819性猛交| 午夜在线视频一区二区区别| 欧美日韩国产片| 亚洲国产中文字幕在线观看| 精品盗摄一区二区三区| 中文精品99久久国产香蕉| 亚洲天堂成人在线观看| 夜夜爽99久久国产综合精品女不卡| 99精品国产在热久久| 欧美一区激情视频在线观看| 亚洲精品乱码久久久久久蜜桃麻豆| 亚洲欧美资源在线| 欧美在线观看一二区| 国产欧美一区二区精品忘忧草| 一个色综合av| 久久国产精品久久精品国产| 国产午夜亚洲精品羞羞网站| 欧美伊人久久| 亚洲人成在线播放| 欧美一区二区性| 亚洲激情av在线| 国产精品美女久久久免费| 欧美一区午夜精品| 999在线观看精品免费不卡网站| 午夜激情亚洲| 99亚洲视频| 亚洲国产精品va在线看黑人| 欧美日韩亚洲一区二区三区在线观看| 亚洲免费婷婷| 亚洲人成网站在线播| 亚洲欧美视频在线观看| 一区三区视频| 国产酒店精品激情| 欧美三区不卡| 欧美激情网友自拍| 免费日韩成人| 欧美成人黄色小视频| 久久精品国产免费观看| 亚洲欧美影音先锋| 亚洲一区二区三区在线| 亚洲激情精品| 亚洲人体大胆视频| 亚洲精品一区在线观看| 亚洲第一色在线| 国产午夜精品久久久久久久| 欧美午夜激情在线| 国产精品黄色在线观看| 国产精品yjizz| 国产精品国产成人国产三级| 欧美激情一区二区在线| 欧美成人自拍| 国产精品theporn| 国产午夜精品久久久| 亚洲人成在线观看一区二区| 亚洲人成人99网站| 亚洲精品之草原avav久久| 一区二区三区鲁丝不卡| 亚洲一区二区精品在线| 亚洲伊人网站| 麻豆精品传媒视频| 一本色道久久综合| 欧美中文字幕久久| 欧美国产另类| 99av国产精品欲麻豆| 亚洲欧美激情一区二区| 久久久久国色av免费看影院| 欧美激情精品久久久久久久变态| 亚洲理论在线| 免费的成人av| 国内在线观看一区二区三区| 亚洲精品乱码久久久久久久久| 亚洲欧美日韩国产一区二区三区| 免费短视频成人日韩| 亚洲永久免费av| 欧美日韩在线免费观看| 亚洲国产精品黑人久久久| 欧美亚洲视频| 亚洲素人在线| 欧美系列一区| 性欧美暴力猛交69hd| 亚洲精品日韩久久| 欧美激情综合五月色丁香| 亚洲第一区在线观看| 久久中文欧美| 老司机精品视频一区二区三区| 国产婷婷一区二区| 欧美日韩国产123| 亚洲精品欧美日韩专区| 亚洲第一精品夜夜躁人人躁| 久久午夜国产精品| 亚洲国产欧美精品| 亚洲国产你懂的| 欧美日本高清| 亚洲欧美日韩综合国产aⅴ| 亚洲一区三区视频在线观看| 国产精品一级在线| 久久漫画官网| 欧美黑人多人双交| 亚洲欧美中文在线视频| 欧美诱惑福利视频| 日韩午夜视频在线观看| 中文精品视频| 悠悠资源网亚洲青| 一区二区三区高清不卡| 国产综合在线视频| 亚洲剧情一区二区| 伊人久久综合| 中国成人黄色视屏| 亚洲第一主播视频| 午夜激情一区| 亚洲你懂的在线视频| 免费国产一区二区| 久久人人超碰| 国产亚洲日本欧美韩国| 日韩视频一区| aⅴ色国产欧美| 欧美精品999| 欧美jizz19性欧美| 狠狠入ady亚洲精品经典电影| 日韩视频在线观看一区二区| 亚洲国产欧美一区二区三区久久 | 久久精品在线| 蜜臀va亚洲va欧美va天堂| 久久国产综合精品| 国产亚洲精品自拍| 欧美一级专区| 精品动漫一区| 久久婷婷国产综合精品青草| 欧美一区二区视频观看视频|