成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

Linux多線程同步機制—讀寫鎖(Read-Write Lock)

系統 Linux
讀寫鎖相比于傳統的互斥鎖(mutex)具有更高的并發性能,特別是在讀操作遠多于寫操作的場景下。這是因為讀寫鎖允許多個讀線程同時訪問共享資源,從而減少了線程間的等待時間,提高了系統的整體吞吐量。

概述

讀寫鎖(Read-Write Lock)是一種線程同步機制,用于管理對共享資源的訪問。與互斥鎖(Mutex)不同,讀寫鎖允許多個線程同時以讀模式訪問共享資源,但只允許一個線程以寫模式訪問資源。這種機制特別適用于讀操作遠多于寫操作的場景,可以顯著提高程序的并發性能。

讀寫鎖原理

讀寫鎖的設計基于以下原則:

  • 讀操作共享:允許多個讀線程同時訪問共享資源,只要沒有寫線程正在訪問或等待訪問資源。
  • 寫操作排他:在任何時候,只允許一個寫線程訪問共享資源。在寫線程持有鎖期間,所有的讀線程和寫線程都將被阻塞。

讀寫鎖內部實現機制

讀寫鎖的內部實現通常依賴于一個或多個底層鎖和一些額外的狀態信息。以下是一種常見的實現方式:

  • 計數器:用于跟蹤當前有多少讀線程正在持有讀鎖。通常,當計數器大于 0 時,表示有讀線程正在訪問資源,此時不允許寫線程獲取鎖;當計數器為 0 時,表示沒有讀線程持有鎖,寫線程可以嘗試獲取鎖。
  • 寫鎖標志:用于標記是否有寫線程正在持有鎖或者有寫線程正在等待獲取鎖。當寫鎖標志為真時,所有讀線程和寫線程都將被阻塞,直到寫線程釋放鎖。
  • 底層互斥鎖和條件變量:讀寫鎖通常會使用一個互斥鎖來保護其內部狀態(如計數器和寫鎖標志),以及一個或多個條件變量來實現線程間的等待和喚醒機制。

讀寫鎖的典型實現

在 Linux 和 POSIX 兼容的系統中,讀寫鎖通常通過 pthread_rwlock_t 類型實現。其內部可能包含如下組件:

  • 互斥鎖(Mutex):用于保護讀寫鎖的內部狀態,如讀計數器和寫鎖狀態。
  • 讀計數器(Read Counter):記錄當前持有讀鎖的線程數量。
  • 條件變量(Condition Variable):用于實現線程的等待和通知機制。通常,會有兩個條件變量,一個用于讀線程,一個用于寫線程。

當線程嘗試獲取讀鎖時,它會檢查寫鎖狀態和讀計數器,如果當前沒有寫線程正在訪問資源,則增加讀計數器并允許讀線程繼續;如果存在寫操作,則讀線程將被阻塞,直到寫操作完成。

類似地,當線程嘗試獲取寫鎖時,它會檢查讀計數器和寫鎖狀態。如果當前沒有讀線程和寫線程正在訪問資源,則設置寫鎖狀態并允許寫線程繼續;如果有讀線程或寫線程正在訪問資源,則寫線程將被阻塞,直到所有讀線程和前一個寫線程完成操作。

讀寫鎖相關API

當然,我會補充完整上面的程序,并進一步完善API函數的描述。請注意,由于程序中的線程是無限循環的,為了示例的完整性,我將添加一個全局變量作為退出條件。此外,我將更詳細地解釋API函數的使用。

pthread_rwlock_init -- 初始化讀寫鎖

int pthread_rwlock_init(pthread_rwlock_t *rwlock, const pthread_rwlockattr_t *attr);
  • 參數:

rwlock:指向要初始化的讀寫鎖變量的指針。

attr:(可選)指向讀寫鎖屬性的指針。如果傳遞NULL,則使用默認屬性。

  • 返回值:成功時返回0,失敗時返回錯誤碼。

pthread_rwlock_destroy -- 銷毀讀寫鎖

int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);
  • 參數:

rwlock:指向要銷毀的讀寫鎖變量的指針。

  • 返回值:成功時返回0,失敗時返回錯誤碼。

pthread_rwlock_rdlock -- 加讀鎖

int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);
  • 參數:

rwlock:指向讀寫鎖變量的指針。

  • 返回值:成功時返回0,失敗時返回錯誤碼。如果鎖被其他線程以寫模式持有,則調用線程將被阻塞。

pthread_rwlock_wrlock -- 加寫鎖

int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);
  • 參數:

rwlock:指向讀寫鎖變量的指針。

  • 返回值:成功時返回0,失敗時返回錯誤碼。如果鎖被其他線程以讀模式或寫模式持有,則調用線程將被阻塞。

pthread_rwlock_tryrdlock -- 嘗試加讀鎖(非阻塞)

int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock);
  • 參數:

rwlock:指向讀寫鎖變量的指針。

  • 返回值:成功時返回0,如果鎖不可用,則返回EBUSY。

C 語言實現讀寫鎖

一、封裝POSIX 線程庫的讀寫鎖

封裝 POSIX 線程庫提供的pthread_rwlock_t類型的讀寫鎖,以及相關的操作函數pthread_rwlock_rdlock、pthread_rwlock_wrlock等,即可實現簡單的讀寫鎖,無需自行實現復雜的邏輯。以下是一個簡單的 C 語言實現讀寫鎖的代碼:

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>

// 定義讀寫鎖結構體
typedef struct {
    pthread_rwlock_t rwlock; // 使用 POSIX 的讀寫鎖
} rwlock_t;

// 定義線程參數結構體
typedef struct thread_params {
    rwlock_t *lock;
    int id; // 線程標識符
} thread_params_t;

// 初始化讀寫鎖
void rwlock_init(rwlock_t *lock) {
    pthread_rwlock_init(&lock->rwlock, NULL);
}

// 銷毀讀寫鎖并釋放資源
void rwlock_destroy(rwlock_t *lock) {
    pthread_rwlock_destroy(&lock->rwlock);
}

// 獲取讀鎖
void rwlock_read_lock(rwlock_t *lock) {
    pthread_rwlock_rdlock(&lock->rwlock);
}

// 釋放讀鎖
void rwlock_read_unlock(rwlock_t *lock) {
    pthread_rwlock_unlock(&lock->rwlock);
}

// 獲取寫鎖
void rwlock_write_lock(rwlock_t *lock) {
    pthread_rwlock_wrlock(&lock->rwlock);
}

// 釋放寫鎖
void rwlock_write_unlock(rwlock_t *lock) {
    pthread_rwlock_unlock(&lock->rwlock);
}

// 讀者線程函數
void *reader(void *arg) {
    thread_params_t *params = arg; // 從參數中獲取線程參數結構體
    int i;
    for (i = 0; i < 3; i++) {
        rwlock_read_lock(params->lock);
        printf("讀者線程 %d: 正在讀取...\n", params->id);
        usleep(100000);
        rwlock_read_unlock(params->lock);
    }
    return NULL;
}

// 寫者線程函數
void *writer(void *arg) {
    thread_params_t *params = arg; // 從參數中獲取線程參數結構體
    int i;
    for (i = 0; i < 5; i++) {
        rwlock_write_lock(params->lock);
        printf("寫者線程 %d: 正在寫入...\n", params->id);
        usleep(500000);
        rwlock_write_unlock(params->lock);
    }
    return NULL;
}

int main() {
    rwlock_t lock;
    rwlock_init(&lock);

    pthread_t threads[5];
    thread_params_t thread_params[5]; // 定義線程參數數組

    int i;

    // 初始化線程參數數組
    for (i = 0; i < 5; i++) {
        thread_params[i].lock = &lock;
        thread_params[i].id = i + 1; // 分配線程標識符
    }

    // 創建讀者線程
    for (i = 0; i < 4; i++) {
        pthread_create(&threads[i], NULL, reader, &thread_params[i]);
    }

    // 創建寫者線程
    pthread_create(&threads[4], NULL, writer, &thread_params[4]);

    // 加入所有線程,等待它們完成
    for (i = 0; i < 5; i++) {
        pthread_join(threads[i], NULL);
    }

    rwlock_destroy(&lock);

    return 0;
}

注意:封裝 rwlock_t 結構體的主要原因在于提供一個清晰、模塊化的接口來管理和使用讀寫鎖,這帶來了以下幾方面的優勢:

  1. 封裝細節:

將讀寫鎖的實現細節封裝在rwlock_t結構體內,對外只暴露必要的接口(如初始化、銷毀、讀鎖和寫鎖操作)。這樣做的好處是隱藏了內部實現的復雜性,外部調用者只需要關注如何使用鎖,而不必關心鎖的具體實現。

  1. 類型安全:

使用專門的結構體類型rwlock_t來表示讀寫鎖,增強了類型安全。這意味著在使用讀寫鎖的地方,編譯器可以檢查是否正確使用了讀寫鎖相關的函數,避免了類型錯誤。

  1. 可擴展性:

如果將來需要改變讀寫鎖的實現方式,比如從使用pthread_rwlock_t切換到另一種鎖機制,只需修改rwlock_t結構體和相關操作函數,而無需修改所有使用讀寫鎖的地方。這大大提高了代碼的可維護性和可擴展性。

  1. 代碼組織與重用:

封裝讀寫鎖的操作在一個獨立的結構體和一組函數中,使得代碼更加整潔、有條理。此外,這樣的封裝有利于代碼的重用,如果項目中其他地方也需要使用讀寫鎖,可以直接引用rwlock_t及相關函數,無需重復編寫相同的代碼。

  1. 測試與調試便利性:

將讀寫鎖的創建、使用和銷毀操作集中在一個結構體及其相關函數中,方便了單元測試和調試??梢元毩⒌販y試讀寫鎖的功能,確保其在各種情況下的正確性。

綜上所述,封裝rwlock_t結構體是軟件工程中一種常見的抽象和封裝機制,它不僅提高了代碼的可讀性和可維護性,也增強了系統的靈活性和健壯性。

這個示例中的關鍵點詳細闡述如下:

1. 讀寫鎖結構體定義 (rwlock_t)

  • 定義:
typedef struct {
    pthread_rwlock_t rwlock; // 使用 POSIX 的讀寫鎖
} rwlock_t;
  • 解析:

rwlock_t結構體封裝了一個pthread_rwlock_t類型的讀寫鎖實例。

pthread_rwlock_t 是 POSIX 標準中定義的一種高級鎖機制,允許同時存在多個讀操作,但寫操作是排他的,確保了數據在并發訪問時的一致性。

2. 線程參數結構體定義 (thread_params_t)

  • 定義:
typedef struct thread_params {
    rwlock_t *lock;
    int id; // 線程標識符
} thread_params_t;
  • 解析:

thread_params_t 結構體用于存儲線程運行所需的信息,包括指向讀寫鎖的指針和線程的唯一標識符。

這種設計允許線程函數以統一的方式接收參數,增強代碼的可讀性和可維護性。

3. 讀寫鎖操作函數

  • 初始化與銷毀:
void rwlock_init(rwlock_t *lock);
void rwlock_destroy(rwlock_t *lock);

rwlock_init 負責初始化讀寫鎖,確保其處于可用狀態。

rwlock_destroy 用于清理鎖資源,避免內存泄漏。

解析:

  • 讀鎖操作:
void rwlock_read_lock(rwlock_t *lock);
void rwlock_read_unlock(rwlock_t *lock);
  • rwlock_read_lock 獲取讀鎖,允許多個讀線程同時訪問共享資源。

  • rwlock_read_unlock 釋放讀鎖,使其他線程有機會獲取鎖。

  • 解析:

  • 寫鎖操作:

    void rwlock_write_lock(rwlock_t *lock);
    void rwlock_write_unlock(rwlock_t *lock);

  • rwlock_write_lock 獲取寫鎖,確保寫操作的排他性,防止數據競爭。

  • rwlock_write_unlock 釋放寫鎖,使其他線程可以繼續執行。

  • 解析:

4. 線程函數

  • 讀者線程:
void *reader(void *arg);

讀者線程函數 reader 接收一個 thread_params_t 類型的參數,從中提取讀寫鎖和線程標識符。

線程執行多次讀操作,每次讀取前獲取讀鎖,讀取后釋放讀鎖。

解析:

  • 寫者線程:
void *writer(void *arg);
  • 寫者線程函數 writer 同樣接收 thread_params_t 類型的參數。

  • 線程執行多次寫操作,每次寫入前獲取寫鎖,寫入后釋放寫鎖。

  • 解析:

5. 主函數 (main)

  • 初始化與線程創建:

初始化讀寫鎖。

創建線程參數數組,為每個線程分配唯一標識符和讀寫鎖引用。

使用pthread_create 創建 4 個讀者線程和 1 個寫者線程。

  • 線程同步與資源清理:
  • 使用 pthread_join 確保所有線程完成后再繼續執行。

  • 調用 rwlock_destroy 銷毀讀寫鎖,釋放相關資源。

6. 線程標識符

  • 功能:

每個線程擁有一個從 1 開始的唯一標識符,便于在日志和調試信息中區分不同線程。

7. POSIX讀寫鎖機制

  • 優勢:

支持多個讀線程的同時訪問,提高了讀密集型應用的并發性能。

確保寫操作的排他性,避免數據損壞,適用于寫操作較少的場景。

8. 線程創建與管理

  • 細節:

使用pthread_create創建線程,傳入線程函數和參數。

利用pthread_join等待線程結束,保證程序的正確性和資源的有序釋放。

9. 資源管理

  • 重要性:

通過初始化和銷毀讀寫鎖,確保了鎖資源的生命周期管理,避免了內存泄漏和資源浪費。

編譯并執行程序,結果如下:

[root@localhost rwlock]# gcc pthread_rwlock.c -o pthread_rwlock -lpthread
[root@localhost rwlock]# ls
pthread_rwlock  pthread_rwlock.c
[root@localhost rwlock]# ./pthread_rwlock
讀者線程 1: 正在讀取...
讀者線程 2: 正在讀取...
讀者線程 3: 正在讀取...
讀者線程 4: 正在讀取...
讀者線程 1: 正在讀取...
讀者線程 2: 正在讀取...
讀者線程 4: 正在讀取...
讀者線程 3: 正在讀取...
讀者線程 1: 正在讀取...
讀者線程 2: 正在讀取...
讀者線程 4: 正在讀取...
讀者線程 3: 正在讀取...
寫者線程 5: 正在寫入...
寫者線程 5: 正在寫入...
寫者線程 5: 正在寫入...
寫者線程 5: 正在寫入...
寫者線程 5: 正在寫入...

通過深入解析上述代碼和結果,我們可以更全面地理解基于 POSIX 讀寫鎖的多線程程序設計策略,以及如何有效利用鎖機制來提高并發應用的性能和可靠性。

二、自定義實現的典型讀寫鎖

自定義實現讀寫鎖代碼需要開發者更深入地理解讀寫鎖的底層實現原理。完全自行實現讀寫鎖的邏輯,通過互斥鎖、條件變量以及自定義的讀計數和寫標志來管理讀寫操作的同步。以下是一段 C 語言實現自定義實現讀寫鎖的代碼:

#include <pthread.h>
#include <stdio.h>

// 定義讀寫鎖結構體
typedef struct rwlock {
    pthread_mutex_t lock;  // 互斥鎖,用于保護讀寫鎖的內部狀態
    pthread_cond_t read_cond;  // 條件變量,用于讀操作的等待和通知
    pthread_cond_t write_cond;  // 條件變量,用于寫操作的等待和通知
    int read_count;  // 讀操作的計數
    int write_in_progress;  // 寫操作是否正在進行的標志
} rwlock_t;

// 初始化讀寫鎖
void rwlock_init(rwlock_t *rwlock) {
    // 初始化互斥鎖
    pthread_mutex_init(&rwlock->lock, NULL);
    // 初始化讀操作的條件變量
    pthread_cond_init(&rwlock->read_cond, NULL);
    // 初始化寫操作的條件變量
    pthread_cond_init(&rwlock->write_cond, NULL);
    // 初始時讀計數為 0
    rwlock->read_count = 0;
    // 初始時寫操作未進行
    rwlock->write_in_progress = 0;
}

// 讀鎖加鎖
void rwlock_read_lock(rwlock_t *rwlock) {
    // 獲取互斥鎖
    pthread_mutex_lock(&rwlock->lock);
    // 若有寫操作正在進行,讀線程等待
    while (rwlock->write_in_progress) {
        pthread_cond_wait(&rwlock->read_cond, &rwlock->lock);
    }
    // 讀計數增加
    rwlock->read_count++;
    // 釋放互斥鎖
    pthread_mutex_unlock(&rwlock->lock);
}

// 讀鎖解鎖
void rwlock_read_unlock(rwlock_t *rwlock) {
    // 獲取互斥鎖
    pthread_mutex_lock(&rwlock->lock);
    // 讀計數減少
    rwlock->read_count--;
    // 若讀計數為 0 且無寫操作正在進行,通知寫線程
    if (rwlock->read_count == 0 && rwlock->write_in_progress == 0) {
        pthread_cond_signal(&rwlock->write_cond);
    }
    // 釋放互斥鎖
    pthread_mutex_unlock(&rwlock->lock);
}

// 寫鎖加鎖
void rwlock_write_lock(rwlock_t *rwlock) {
    // 獲取互斥鎖
    pthread_mutex_lock(&rwlock->lock);
    // 若有讀操作或寫操作正在進行,寫線程等待
    while (rwlock->read_count > 0 || rwlock->write_in_progress) {
        pthread_cond_wait(&rwlock->write_cond, &rwlock->lock);
    }
    // 標記寫操作正在進行
    rwlock->write_in_progress = 1;
    // 釋放互斥鎖
    pthread_mutex_unlock(&rwlock->lock);
}

// 寫鎖解鎖
void rwlock_write_unlock(rwlock_t *rwlock) {
    // 獲取互斥鎖
    pthread_mutex_lock(&rwlock->lock);
    // 標記寫操作結束
    rwlock->write_in_progress = 0;
    // 通知所有等待讀的線程
    pthread_cond_broadcast(&rwlock->read_cond);
    // 通知等待寫的線程
    pthread_cond_signal(&rwlock->write_cond);
    // 釋放互斥鎖
    pthread_mutex_unlock(&rwlock->lock);
}

// 讀線程的操作函數
void reader_function(rwlock_t *rwlock) {
    rwlock_read_lock(rwlock);
    printf("Reader is reading...\n");
    rwlock_read_unlock(rwlock);
}

// 寫線程的操作函數
void writer_function(rwlock_t *rwlock) {
    rwlock_write_lock(rwlock);
    printf("Writer is writing...\n");
    rwlock_write_unlock(rwlock);
}

int main() {
    rwlock_t rwlock;  // 定義讀寫鎖變量
    rwlock_init(&rwlock);  // 初始化讀寫鎖

    pthread_t reader1, reader2, writer;  // 定義線程變量

    // 創建讀線程 1
    pthread_create(&reader1, NULL, (void *)reader_function, &rwlock);
    // 創建讀線程 2
    pthread_create(&reader2, NULL, (void *)reader_function, &rwlock);
    // 創建寫線程
    pthread_create(&writer, NULL, (void *)writer_function, &rwlock);

    // 等待讀線程 1 結束
    pthread_join(reader1, NULL);
    // 等待讀線程 2 結束
    pthread_join(reader2, NULL);
    // 等待寫線程結束
    pthread_join(writer, NULL);

    return 0;
}

以下是對這段代碼的詳細解析:

1.包含頭文件

#include <pthread.h>:包含了 POSIX 線程庫的頭文件,用于多線程編程。

#include <stdio.h>:包含了標準輸入輸出頭文件,用于打印輸出。

2.定義讀寫鎖結構體 rwlock_t

  • pthread_mutex_t lock:用于保護讀寫鎖內部狀態的互斥鎖。

  • pthread_cond_t read_cond:用于讀操作等待和通知的條件變量。

  • pthread_cond_t write_cond:用于寫操作等待和通知的條件變量。

  • int read_count:記錄讀操作的數量。

  • int write_in_progress:標志是否有寫操作正在進行。

3.函數定義

  • rwlock_init 函數:初始化讀寫鎖的各個成員,包括互斥鎖和條件變量,將讀計數設為0,寫標志設為0。

  • rwlock_read_lock 函數:獲取讀鎖。先獲取互斥鎖,若有寫操作正在進行則等待,然后增加讀計數,最后釋放互斥鎖。

  • rwlock_read_unlock 函數:釋放讀鎖。先獲取互斥鎖,減少讀計數,若讀計數為 0 且無寫操作正在進行則通知寫線程,最后釋放互斥鎖。

  • rwlock_write_lock 函數:獲取寫鎖。先獲取互斥鎖,若有讀操作或寫操作正在進行則等待,然后設置寫標志為 1,最后釋放互斥鎖。

  • rwlock_write_unlock 函數:釋放寫鎖。先獲取互斥鎖,清除寫標志,通知所有等待讀的線程和一個等待寫的線程,最后釋放互斥鎖。

  • reader_function 函數:讀線程的執行函數,獲取讀鎖后打印信息然后釋放讀鎖。

  • writer_function 函數:寫線程的執行函數,獲取寫鎖后打印信息然后釋放寫鎖。

4.main 函數

  • 定義讀寫鎖變量 rwlock 并初始化。

  • 定義線程變量 reader1、reader2 和 writer。

  • 創建兩個讀線程和一個寫線程,并分別指定執行函數和傳遞讀寫鎖參數。

  • 使用 pthread_join 等待三個線程結束,確保程序不會提前退出。總的來說,這段代碼實現了一個簡單的讀寫鎖機制,通過多線程的方式展示了讀線程和寫線程對共享資源的同步訪問。

編譯并執行程序,結果如下:

[root@localhost rwlock]# gcc pthread_rwlock_st.c -o pthread_rwlock_st -lpthread
[root@localhost rwlock]# ls
pthread_rwlock  pthread_rwlock.c  pthread_rwlock_st  pthread_rwlock_st.c
[root@localhost rwlock]# ./pthread_rwlock_st
Reader is reading...
Reader is reading...
Writer is writing...

總結

讀寫鎖相比于傳統的互斥鎖(mutex)具有更高的并發性能,特別是在讀操作遠多于寫操作的場景下。這是因為讀寫鎖允許多個讀線程同時訪問共享資源,從而減少了線程間的等待時間,提高了系統的整體吞吐量。

然而,讀寫鎖也有其局限性,例如在寫操作頻繁的場景下,由于寫操作的排他性,可能會導致大量的讀線程被阻塞,降低系統的并發性能。因此,在設計多線程應用時,選擇合適的同步機制是非常重要的。

責任編輯:武曉燕 來源: Linux二進制
相關推薦

2024-06-28 08:45:58

2024-07-05 08:32:36

2010-01-21 11:27:30

linux多線程機制線程同步

2010-03-15 16:31:34

Java多線程

2011-11-23 10:09:19

Java線程機制

2022-03-24 13:36:18

Java悲觀鎖樂觀鎖

2024-07-25 11:53:53

2022-06-15 07:32:35

Lock線程Java

2023-06-09 07:59:37

多線程編程鎖機制

2017-12-15 10:20:56

MySQLInnoDB同步機制

2019-05-27 14:40:43

Java同步機制多線程編程

2025-03-31 00:01:12

2012-07-27 10:02:39

C#

2012-07-09 09:25:13

ibmdw

2010-01-21 11:23:49

Linux多線程同步消息隊列

2016-09-20 15:21:35

LinuxInnoDBMysql

2010-01-21 11:22:35

Linux多線程同步

2023-10-08 09:34:11

Java編程

2011-06-22 13:57:54

Java多線程

2011-06-22 13:47:16

Java多線程
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 午夜性色a√在线视频观看9 | 涩涩99| 久久久久国产一级毛片高清网站 | 欧美影院久久 | 在线观看午夜视频 | 日日干日日色 | 一区二区三区观看视频 | 欧美精品一区二区在线观看 | 一区二区三区影院 | www久久爱| 一区二区三区四区在线视频 | 国产精品视频在线播放 | 操操日| 国产日韩欧美另类 | 欧美日韩综合视频 | 久久久久久看片 | 手机av在线| 在线国产一区二区 | 99亚洲| 午夜小视频免费观看 | 欧美日韩亚洲三区 | 日韩中文字幕在线 | 亚洲国产一区二区视频 | 久久在线精品 | 欧美日韩在线观看视频网站 | 一区二区三区欧美大片 | 99视频在线免费观看 | 天天拍天天操 | 99成人精品| 伊人春色成人网 | 一区二区在线看 | 91精品久久久久久久久久入口 | 亚洲手机在线 | 久久久久1 | 日韩另类| 欧美黄色网| 亚洲国产精品久久久久婷婷老年 | 日韩av在线不卡 | 国产特级毛片aaaaaa | 久久久久国产 | 国产成人综合一区二区三区 |