1. 程式人生 > >一起來寫web server 04 -- 執行緒池版本

一起來寫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析構了,鎖就解開了,程式碼很漂亮.

總結