#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <errno.h>
#include <stdlib.h>
#include <time.h>
#include <pthread.h>
void* testThreadPool(int *t);
pthread_mutex_t clifd_mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t clifd_cond = PTHREAD_COND_INITIALIZER;
int a = 0;
int main() {
int sock_fd, conn_fd;
int optval;
socklen_t cli_len;
struct sockaddr_in cli_addr, serv_addr;
sock_fd = socket(AF_INET, SOCK_STREAM, 0);
if (sock_fd < 0) {
printf("socket\n");
}
optval = 1;
if (setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR, (void *) &optval,
sizeof(int)) < 0) {
printf("setsockopt\n");
}
memset(&serv_addr, 0, sizeof(struct sockaddr_in));
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(4507);
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
if (bind(sock_fd, (struct sockaddr *) &serv_addr,
sizeof(struct sockaddr_in)) < 0) {
printf("bind\n");
}
if (listen(sock_fd, 100) < 0) {
printf("listen\n");
}
cli_len = sizeof(struct sockaddr_in);
int t;
pthread_t * mythread;
mythread = (pthread_t*) malloc(100 * sizeof(pthread_t));
for (t = 0; t < 5; t++) {
int *i=(int*)malloc(sizeof(int));
*i=t;
if (pthread_create(&mythread[t], NULL, (void*)testThreadPool, (void*)i) != 0) {
printf("pthread_create");
}
}
while (1) {
conn_fd = accept(sock_fd, (struct sockaddr *) &cli_addr, &cli_len);
if (conn_fd < 0) {
printf("accept\n");
}
printf("accept a new client, ip:%s\n", inet_ntoa(cli_addr.sin_addr));
pthread_mutex_lock(&clifd_mutex);
a=conn_fd;
pthread_cond_signal(&clifd_cond);
pthread_mutex_unlock(&clifd_mutex);
}
return 0;
}
void* testThreadPool(int *t) {
printf("t is %d\n", *t);
for (;;) {
pthread_mutex_lock(&clifd_mutex);
pthread_cond_wait(&clifd_cond, &clifd_mutex);
printf("a is %d\n", a);
printf("t is %d\n", *t);
pthread_mutex_unlock(&clifd_mutex);
sleep(100);
}
return (void*) 0;
}
瞭解 pthread_cond_wait() 的作用非常重要 -- 它是 POSIX 線程信號發送系統的核心,也是最難以理解的部分。
首先,讓我們考慮以下情況:線程爲查看已鏈接列表而鎖定了互斥對象,然而該列表恰巧是空的。這一特定線程什麼也幹不了 -- 其設計意圖是從列表中除去節點,但是現在卻沒有節點。因此,它只能:
鎖定互斥對象時,線程將調用 pthread_cond_wait(&mycond,&mymutex)。pthread_cond_wait() 調用相當複雜,因此我們每次只執行它的一個操作。
pthread_cond_wait() 所做的第一件事就是同時對互斥對象解鎖(於是其它線程可以修改已鏈接列表),並等待條件 mycond 發生(這樣當 pthread_cond_wait() 接收到另一個線程的“信號”時,它將甦醒)。現在互斥對象已被解鎖,其它線程可以訪問和修改已鏈接列表,可能還會添加項。 【要求解鎖並阻塞是一個原子操作】
此時,pthread_cond_wait() 調用還未返回。對互斥對象解鎖會立即發生,但等待條件 mycond 通常是一個阻塞操作,這意味着線程將睡眠,在它甦醒之前不會消耗 CPU 週期。這正是我們期待發生的情況。線程將一直睡眠,直到特定條件發生,在這期間不會發生任何浪費 CPU 時間的繁忙查詢。從線程的角度來看,它只是在等待 pthread_cond_wait() 調用返回。
現在繼續說明,假設另一個線程(稱作“2 號線程”)鎖定了 mymutex 並對已鏈接列表添加了一項。在對互斥對象解鎖之後,2 號線程會立即調用函數 pthread_cond_broadcast(&mycond)。此操作之後,2 號線程將使所有等待 mycond 條件變量的線程立即甦醒。這意味着第一個線程(仍處於 pthread_cond_wait() 調用中)現在將甦醒。
現在,看一下第一個線程發生了什麼。您可能會認爲在 2 號線程調用 pthread_cond_broadcast(&mymutex) 之後,1 號線程的 pthread_cond_wait() 會立即返回。不是那樣!實際上,pthread_cond_wait() 將執行最後一個操作:重新鎖定 mymutex。一旦 pthread_cond_wait() 鎖定了互斥對象,那麼它將返回並允許 1 號線程繼續執行。那時,它可以馬上檢查列表,查看它所感興趣的更改。
停止並回顧!
那個過程非常複雜,因此讓我們先來回顧一下。第一個線程首先調用:
pthread_mutex_lock(&mymutex);
然後,它檢查了列表。沒有找到感興趣的東西,於是它調用:
pthread_cond_wait(&mycond, &mymutex);
然後,pthread_cond_wait() 調用在返回前執行許多操作:
pthread_mutex_unlock(&mymutex);
它對 mymutex 解鎖,然後進入睡眠狀態,等待 mycond 以接收 POSIX 線程“信號”。一旦接收到“信號”(加引號是因爲我們並不是在討論傳統的 UNIX 信號,而是來自 pthread_cond_signal() 或 pthread_cond_broadcast() 調用的信號),它就會甦醒。但 pthread_cond_wait() 沒有立即返回 -- 它還要做一件事:重新鎖定 mutex:
pthread_mutex_lock(&mymutex);
pthread_cond_wait() 知道我們在查找 mymutex “背後”的變化,因此它繼續操作,爲我們鎖定互斥對象,然後才返回。