2016-01-13 68 views
12

我正在測試一個生產者 - 消費者的場景,當生產者試圖在已滿的隊列上寫入時,它會阻塞。我想測試生產者線程是否正確喚醒,並在消費者從完整隊列*中讀取後按預期工作。隊列寫入API在檢測到隊列已滿時調用pthread_cond_wait(),並且在讀取隊列後,讀取API將通知條件變量。如何安排pthread_cond_signal(),使其始終在另一個線程上遵循pthread_cond_wait()?

如何確保在我的測試環境中執行任何其他操作序列時發生的序列3

enter image description here

* 是的,我想單獨測試這個有限的情況下;還有其他一些測試來測試整個隊列的功能,除了這些測試外,還有其他測試。

更多細節 -
沒有關於隊列中的單個互斥。有 是2個條件變量 - 一個信號寫入(全部寫入),一個到 信號讀取(全部讀取)。如果隊列已滿,則queue_write API將在讀取 condvar上阻塞。如果隊列爲空,則隊列讀取API塊 寫入condvar。所有信號發生在互斥體的主密鑰 下。
隊列中有更多的細微差別,但爲了設置上下文這個問題的目的,這是一個 充分的隊列功能總結。

+0

只啓動生產者線程,等到它被阻止,然後啓動消費者? – EOF

+0

是否有任何API函數可以讓您只要隊列滿就測試? – Superlokkus

+0

@EOF - 如何以編程方式發現生產者線程現在被阻止? (我正在尋找超出「*等待足夠的時間*」的東西) –

回答

1

編輯(請注意,省略了並行線程調用的錯誤處理)

您可以通過測試,如果隊列滿了,你在評論中提到的功能實現這一點。對於這個答案,我會假設它是bool is_queue_full(const queue*)

在您的測試案例中,您可以通過創建生產者並創建消費者來保證場景3,當且僅當隊列已滿時。像 bool is_queue_full(queue *); //不能使用互斥體本身,也許標誌着它只是實習生使用

struct queue { 
    /* Actual queue stuff */ 
    pthread_mutex_t queue_mutex; 
    pthread_cond_t read_condvar; 
    pthread_cond_t write_condvar; 
}; 

void wait_until_queue_is_full (queue *q) { 

    pthread_mutex_lock(&q->queue_mutex); 
    while (!is_queue_full(q)){ //Use in loop because of Spurious wakeups 
     pthread_cond_wait(&q->write_condvar,&q->queue_mutex); 
    } 
    pthread_mutex_unlock(&q->queue_mutex); 
} 

bool test_writer_woke_up(queue *q); 

bool test_case(){ 
    queue *q = create_queue(); 
    producer *p = create_producer(q); 

    wait_until_queue_is_full(q); 

    return test_writer_woke_up(q); //or cache the result and destroy your queue, but if your testrunner process will quit anyway... 
} 

wait_until_queue_is_full將只檢查隊列已滿,如果沒有,會等待,就像任何讀卡器,直到你的作家又名生產者都有使它充滿。然後你的測試用例可以生成類似於test_writer_woke_up void intern_consume_stuff(隊列q)的消費者;/您實習生函數,它的東西從隊列中, 但doesen't在意sychronization又名互斥和condvar */

bool test_writer_woke_up(queue *q){ 
    pthread_mutex_lock(&q->queue_mutex); //Could be omitted in this testcase (together with the 1 unlock below of course) 
    void intern_consume_stuff(queue *q); 
    pthread_mutex_unlock(&q->queue_mutex); //Could be omitted in this testcase (together with the 1 lock above of course) 
    pthread_cond_signal(&q->read_condvar); 

    /* Adjust these as you like to give your producer/writer time to wake up and produce something 
    */ 
    unsigned retry_count = 5; 
    unsigned sleep_time = 1; 

    //timed cond wait approach 
    for (; retry_count > 0; --retry_count){ 
     pthread_mutex_lock(&q->queue_mutex); 
     struct timespec ts; 
     clock_gettime(CLOCK_REALTIME, &ts); 
     ts.tv_sec += sleep_time; 
     int timed_cond_rc = 0; 
     while (!is_queue_full(q) && timed_cond_rc == 0) { 
      timed_cond_rc = pthread_cond_timedwait(&q->write_condvar, &q->queue_mutex, &ts); 
     } 
     if (is_queue_full(q)) { 
      pthread_mutex_unlock(&q->queue_mutex); 
      return true; 
     } 
     assert(timed_cond_rc == ETIMEDOUT); 
     continue; 
    } 
    return false; 
} 

如果已經使用了絕對時間等待,因爲你必須重新計算相關計時,或者把事情簡單化,你可以用這種幼稚的做法代替for循環

//naive busy approach 
for (; retry_count > 0; --retry_count){ 
    pthread_mutex_lock(q->queue_mutex); 
    const bool queue_full_result = is_queue_full(q); 
    pthread_mutex_unlock(q->queue_mutex); 

    if (queue_full_result){ 
     return true; 
    } else { 
     pthread_yield(); 
     sleep(sleep_time); 
    } 
} 
+0

我考慮過這個,但放棄了這個想法,因爲我認爲這是CPU密集型的。我想要一個更清晰的方式來實現這一點,即不忙 - 讀取隊列狀態。 –

+0

這隻有在您想要或被允許或能夠編寫自己的'is_queue_full'時才能實現。此外,忙碌的等待在你的非生產性測試代碼中不應該那麼糟糕。但是由於這似乎是你自己的隊列,所以我們可以編寫一個異步方法。你能否給我你的互斥體名稱和條件來檢查「豐滿度」? – Superlokkus

+0

我得出的結論是,沒有正確的方法做到這一點,而無需修改隊列API本身。所以我更喜歡上面的方法。然而,我想考慮你對'while(!is_queue_full(q))pthread_yield();'的看法 - ' - 這是一種改進嗎? –

2

由於您的隊列使用pthread_cond_signal,必須持有鎖定爲好。所以你的測試用例應該只是保持鎖定,創建制作者,然後等待信號。生產者生成信號後,釋放鎖定並創建一個消費者。

void test() { 
    pthread_mutex_lock(q_lock); 
    //  Blocks on the same queue lock the producer and 
    //  consumer would use. 
    create_producer(); 
    //  The producer will block on the queue lock when 
    //  it tries to write to the queue. 
    do { 
     pthread_cond_wait(q_write_cond, q_lock); 
     // Mimic a blocked queue_read, and wait for the 
     // producer to signal. This will release the lock 
     // and allow the producer to progress. 
    } while (!q_is_full()); 
    //  The queue is now full, lock is held since 
    //  pthread_cond_wait returned. 
    pthread_mutex_unlock(q_lock); 
    //  Release the queue lock, allow the consumer to 
    //  operate unhindered. 
    create_consumer(); 
    //  The consumer will proceed to drain the queue. 
} 
相關問題