一起來寫web server 04 -- 線程池版本


從這個版本開始,後面的代碼差不多是越來越難啦.

這個版本,我們主要是要實現一個線程池版本的web server.這個版本的設計出自UNP.

思想

思想非常簡單,那就是父線程首先構建n多子線程,這些子線程全部爭搶全局的一把鎖,只有搶到了鎖的線程才能夠調用accept函數,否則都會阻塞掉.

代碼

/*- 
* 線程池版本的web server.主要的思想是事先構建一個線程池,只是需要注意的是,accept的時候需要加鎖.
*/

int listenfd; /* 全局的一個監聽套接字 */
MutexLock mutex; /* 全局的一把鎖 */

int main(int argc, char *argv[])
{
    listenfd = Open_listenfd(8080); /* 8080號端口監聽 */
    //signal(SIGPIPE, SIG_IGN); 

    pthread_t tids[10];
    void* thread_main(void *);

    for (int i = 0; i < 10; ++i) {
        int *arg = (int *)Malloc(sizeof(int)); /* 這個東西不會共享 */
        *arg = i;
        Pthread_create(&tids[i], NULL, thread_main, (void *)arg);
    }
    for ( ; ; )
        pause(); 
    return 0;
}

void* thread_main(void *arg)
{
    printf("thread %d starting\n", *(int*)arg);
    Free(arg);
    struct sockaddr cliaddr;
    socklen_t clilen;
    int connfd;
    while (true) {
        {
            MutexLockGuard lock(mutex); /* 加鎖 */
            connfd = Accept(listenfd, &cliaddr, &clilen);
        }
        doit(connfd); /* 處理連接 */
        close(connfd); /* 關閉連接 */
    }
}

一般涉及到多線程的資源共享,鎖或者說互斥,加上一個同步機制,總是逃不開的話題.
對於共享資源的寫,總是要加鎖的.如何來構造一把鎖呢?我這裏的代碼參考了muduo庫的設計.

我們一起來看一下MutexLock這個類.

class MutexLock : noncopyable
{
    private:
        pthread_mutex_t mutex_; /* 這是系統定義的鎖的類型 */
        pid_t holder_; /* 記錄擁有線程的id */
     ...
}

它的構造函數,僅僅是調用普通的鎖的初始化的代碼:

MutexLock()
        : holder_(0)
    {
        pthread_mutex_init(&mutex_, NULL); /* 初始化 */
    }

它的析構函數,主要是調用鎖的銷燬函數.

~MutexLock()
    {
        assert(holder_ == 0);
        pthread_mutex_destroy(&mutex_); /* 銷燬鎖 */
    }

MutexLock這個類巧妙的利用了CPP類的特性來管理鎖這個資源.
接下來比較重要的是加鎖以及解鎖操作:

void lock()
    {
        MCHECK(pthread_mutex_lock(&mutex_));
        assignHolder(); /* 指定擁有者 */
    }

    void unlock()
    {
        unassignHolder(); /* 丟棄擁有者 */
        MCHECK(pthread_mutex_unlock(&mutex_));
    }

如何來使用這個鎖呢?muduo庫設計了另外一個類,叫做MutexLockGuard.這個類非常簡單:

class MutexLockGuard : noncopyable
{
public:
    explicit MutexLockGuard(MutexLock& mutex)
        : mutex_(mutex)
    {
        mutex_.lock(); /* 構造時加鎖 */
    }
    ~MutexLockGuard()
    {
        mutex_.unlock(); /* 析構時解鎖 */
    }
private:
    MutexLock& mutex_; /* 持有鎖的一個引用 */
};

通過這個類,我們就可以很方便的實現加鎖和解鎖操作了,我們只需要向之前代碼裏那樣使用就行了:

{
    MutexLockGuard lock(mutex); /* 加鎖 */
    ...do other thing...
}

在這個中括號包圍的作用域裏,鎖是有效的,出了這個作用域,lock析構了,鎖就解開了,代碼很漂亮.

總結

好了,這個版本的代碼就是這樣啦,感興趣的同學可以到這裏來查看代碼:
https://github.com/lishuhuakai/Spweb

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章