C++多執行緒筆試程式設計題

FreeeLinux發表於2017-02-02

子執行緒迴圈 10 次,接著主執行緒迴圈 100 次,接著又回到子執行緒迴圈 10 次,接著再回到主執行緒又迴圈 100 次,如此迴圈50次,試寫出程式碼。

2017.2.11日更新:
之前寫這幾段程式碼忘記對mutex和condition呼叫pthread_destroy()!

程式碼如下:

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

#define TEST_TIMES_LIMIT 3

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;

typedef enum {MAIN_THREAD, WORKER_THREAD}SWITCH_THREAD;
SWITCH_THREAD target;

void* worker_thread(void* arg)
{
    for(int test=0; test<TEST_TIMES_LIMIT; ++test){
        pthread_mutex_lock(&mutex);
        while(target != WORKER_THREAD)
            pthread_cond_wait(&cond, &mutex);
        //pthread_mutex_unlock()函式和主執行緒中一樣,都可以放在這裡
        //因為下面並不存在race condition,所以放在這裡縮小了臨界區,但效率並無提高
        printf("worker thread:>\n");
        for(int i=1; i<=10; ++i)
    printf("%d ", i); 
        printf("\n");
        target = MAIN_THREAD;
        pthread_mutex_unlock(&mutex);
        pthread_cond_signal(&cond);
    }   
}

void main_thread()
{
    for(int test=0; test<TEST_TIMES_LIMIT; ++test){
        pthread_mutex_lock(&mutex);
        while(target != MAIN_THREAD)
            pthread_cond_wait(&cond, &mutex);
        printf("main thread:>\n");
        for(int i=1; i<=100; ++i)
            printf("%d ", i);
        printf("\n\n");
        target = WORKER_THREAD;
        pthread_mutex_unlock(&mutex);
        pthread_cond_signal(&cond);
    }
}

int main()
{
    target = WORKER_THREAD;

    pthread_t tid;
    int ret = pthread_create(&tid, NULL, worker_thread, NULL);
    assert(ret != -1);
    main_thread();
    pthread_join(tid, NULL);
    return 0;
}

編寫一個程式,開啟3個執行緒,這3個執行緒的ID分別為A、B、C,每個執行緒將自己的ID在螢幕上列印10遍,要求輸出結果必須按ABC的順序顯示;如:ABCABC….依次遞推。

方法一:採用條件變數和互斥鎖

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

#define NUM_OF_CHARACTER 3

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;

static int current_id = 0;

void* worker_thread(void *arg)
{
    int thread_id = *(int *)arg;
    for(int i=0; i<10; ++i){
        pthread_mutex_lock(&mutex);
        while(thread_id != current_id)
            pthread_cond_wait(&cond, &mutex);
        printf("%c", 'A'+thread_id);
        current_id = (current_id + 1) % NUM_OF_CHARACTER;
        pthread_mutex_unlock(&mutex);
        pthread_cond_broadcast(&cond);
    }   
}

int main()
{
    int thread_id[NUM_OF_CHARACTER] = {0, 1, 2}; 
    pthread_t tids[NUM_OF_CHARACTER];
    for(int i=0; i<NUM_OF_CHARACTER; ++i)
        pthread_create(&tids[i], NULL, worker_thread, (void *)(thread_id+i));
    for(int i=0; i<NUM_OF_CHARACTER; ++i)
        pthread_join(tids[i], NULL);

    return 0;
}

注意我的執行緒id是用單獨的陣列儲存並傳遞給執行緒的,如果直接使用for迴圈中的&i,那麼執行緒區域性thread_id會隨著i改變。

方法二:使用原子操作(推薦)

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

#define TEST_TIMES 10
#define NUM_OF_CHARACTERS 3

static volatile int counter = 0;

void* worker_thread(void *arg)
{
    int thread_id = *(int *)arg;
    for(int i=0; i<TEST_TIMES; ++i){
        while(counter % NUM_OF_CHARACTERS != thread_id);
        printf("%c", 'A'+thread_id);
        __sync_fetch_and_add(&counter, 1); 
    }   
}

int main()
{
    int thread_id[NUM_OF_CHARACTERS] = {0, 1, 2}; 
    pthread_t tid[NUM_OF_CHARACTERS];
    for(int i=0; i<NUM_OF_CHARACTERS; ++i)
   pthread_create(&tid[i], NULL, worker_thread, (void *)(thread_id+i));
    for(int i=0; i<NUM_OF_CHARACTERS; ++i)
        pthread_join(tid[i], NULL);
    return 0;
}

讀者寫者問題
這也是一個非常經典的多執行緒題目,題目大意如下:有一個寫者很多讀者,多個讀者可以同時讀檔案,但寫者在寫檔案時不允許有讀者在讀檔案,同樣有讀者讀時寫者也不能寫。

做到這道題為之,我已經不耐煩POSIX執行緒API了,我要拿起我的老本行C++來做了。

#include <iostream>
#include <thread>
#include <condition_variable>
#include <unistd.h>

class rwlock {
public:
    rwlock() : writer_(0), reader_(0), active_(0) {}
public:
    void read_lock() {
        std::unique_lock<std::mutex> lock(lk_);
        ++reader_;    //讀鎖用的時候說明要讀,我們在函式實現中增加讀者數目
        while(active_ < 0  || writer_ > 0) //(1)有人已經佔用資源(2)當前有優先順序更高的寫者執行緒
              rcond_.wait(lock);
        --reader_;
        ++active_;  //注意: 讀鎖是++
    }   
    void write_lock() {
 std::unique_lock<std::mutex> lock(lk_);
        ++writer_;   //同理
        while(active_ != 0)   //注意注意,是不等於0
            wcond_.wait(lock);
        --writer_;
        active_ = -1;  //寫鎖是直接賦為-1
    }   
    void unlock() {
        std::unique_lock<std::mutex> lock(lk_);
        if(active_ > 0){ //解鎖讀鎖
            if(--active_ == 0) //因為有多個讀者,直到讀者數目為0,才喚醒wait狀態的寫者,表示所有讀者讀完了,寫者可以上了
                wcond_.notify_one();
        }   
else{  //到此處說明是解鎖寫鎖
            active_ = 0;  //直接賦值為0
            if(writer_ > 0)  //優先寫
                wcond_.notify_one();
            else if(reader_ > 0)
                rcond_.notify_all(); //可多個同時讀
        }
    }
private:
    std::mutex               lk_;
    std::condition_variable  wcond_;
    std::condition_variable  rcond_;
    unsigned int             writer_;
    unsigned int             reader_;
    int                      active_;
};

void worker_1(const std::shared_ptr<rwlock>& rl)
{
    while(1){
        rl->write_lock();
        std::cout<<"writing start."<<std::endl;
        sleep(5);
        std::cout<<"write done"<<std::endl;
        rl->unlock();
sleep(5);
    }
}

void worker_2(const std::shared_ptr<rwlock>& rl)
{
    while(1){
        rl->read_lock();
        std::cout<<"Jinping Xi reading"<<std::endl;
        sleep(1);
        std::cout<<"Jinping Xi read done"<<std::endl;
        rl->unlock();
    }
}

void worker_3(const std::shared_ptr<rwlock>& rl)
{
    while(1){
        rl->read_lock();
        std::cout<<"Keqiang Li reading"<<std::endl;
        sleep(1);
        std::cout<<"Keqiang Li read done"<<std::endl;
        rl->unlock();
    }
}

int main()
{
    std::shared_ptr<rwlock> rl(new rwlock());
    std::thread th1(worker_1, rl);
    std::thread th2(worker_2, rl);
    std::thread th3(worker_3, rl);
    th1.join();
    th2.join();
    th3.join();
    return 0;
}

由於lock_guard不能直接使用.wait(),所以這裡用的是unique_lock。它們基本功能是一樣的,後者功能更豐富一些。

相關文章