1,多線程服務器
mul_pth_server.c中代碼:
#include <pthread.h> //不要忘記了
#include <stdlib.h>
#include <stdio.h>
#include <signal.h>
#define P_NUMBER 255 //併發進程數量
#define COUNT 5 //每次進程打印字符串個數
#define LOGFILE "logFile.log" //日誌文件
char *s = "my mul_pth_server.c"; //向日志內寫的內容
FILE* plogFile = NULL;
print_log()
{
int i = 0;
for (i = 0; i < P_NUMBER; ++i)
{
printf("[%d] %s\n", i, s); //向標準輸出設備打印數據
fprintf(plogFile, "[%d] %s\n", i, s); //向日志文件輸入數據
}
pthread_exit(0); //線程結束
}
int main()
{
int i = 0;
pthread_t pid[P_NUMBER]; //創建線程數組
plogFile = fopen(LOGFILE, "a+"); //以追加方式打開文件
if (plogFile == NULL)
{
perror("open file failure\n");
exit(1);
}
for (i = 0; i < P_NUMBER; ++i)
{
pthread_create(&pid[i], NULL, (void *)print_log, NULL); //創建線程
}
for (i = 0; i < P_NUMBER; ++i)
{
pthread_join(pid[i], NULL); //回收線程
}
printf("回收完畢\n");
return 0;
}
運行結果(截取部分):
查看logFile.log中的記錄(截取部分)
2,多進程服務器
mul_proc_server.c中代碼:
#include <stdlib.h>
#include <stdio.h>
#include <signal.h>
#define P_NUMBER 255 //併發進程數量
#define COUNT 5 //每次進程打印字符串個數
#define LOGFILE "logFile.log" //日誌文件
int main()
{
int i = 0;
int j = 0;
FILE* plogFile = fopen(LOGFILE, "a+");//以追加方式打開
if (plogFile == NULL)
{
perror("open file failure !\n");
exit(1);
}
for (i = 0; i < P_NUMBER; ++i)
{
if (fork() == 0) //創建子進程,並且進入子進程代碼
{
for (j = 0; j < COUNT; ++j)
{
printf("[%d] %s\n", j ,s);
fprintf(plogFile, "[%d] %s\n", j, s); //向日志文件中>寫入內容
}
exit(2); //子進程結束
}
}
for (i = 0; i < P_NUMBER; ++i) //回收子進程
{
wait(0);
}
printf("回收完畢\n");
return 0;
}
運行結果(截取部分)
查看logFile.log文件(截取部分結果)
3,池的概念
池的概念:
池是提高服務器性能的方法之一。是一種以空間換時間,通過“浪費”服務器的硬件資源,以換取服務器的運行效率。這就是“池”的概念。
池的優點:
池是一組資源的集合,這組資源在服務器啓動之初就被完全創建好並初始化,這稱爲靜態資源分配。當服務器進入正式運行階段,即開始處理客戶請求的時候,如果它需要相關的資源,就可以直接從池中獲取,無需動態分配。
很顯然,直接從池中取得所需資源比動態分配資源的速度要快得多,因爲分配系統資源的系統調用都是很耗時的。當服務器處理完一個客戶連接後,可以把相關的資源放回池中,無需執行系統調用來釋放資源。從最終的效果來看,池相當於服務器管理系統資源的應用層設施,它避免了服務器對內核的頻繁訪問。
池的缺點:
由於池中的資源都是預先靜態分配的,所以無法預期知道應該分配多少資源。
解決這個問題的方案就是分配“足夠多”的資源,即針對每個可能的客戶連接都分配必要的資源。這通常會導致資源的浪費,因爲任一時刻的客戶數量都可能遠遠沒有達到服務器能支持的最大客戶數量。好在這種資源的浪費對服務器來說一般不會構成問題。
還有一種解決方法就是預先分配一定的資源,如果發現不夠用,就再動態分配一些資源加入到池中。
池的分類
根據不同的資源類型,池可以分爲多種,常見的有內存池、進程池、線程池和連接池。
內存池:通常用於socket的接受緩存和發送換緩存。對於某些長度有限的客戶請求,比如HTTP請求,預先分配一個足夠大小(比如500字節)的接受緩存區。當客戶請求的長度超過接受緩存區的大小時,我們可以選擇丟棄請求或者動態擴大接受緩存區。
進程池和線程池都是併發編程常用的“伎倆”。當我們需要一個工作進程或者工作進程來處理新的客戶請求時,我們可以直接從進程池或線程池中取得一個執行實體,而無需動態的調用fork或pthread_create等函數來創建進程或線程。