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等函数来创建进程或线程。