select,poll,epoll都是IO多路复用的机制。I/O多路复用就是通过一种机制,可以监视多个描述符,一旦某个描述符就绪(一般是读就绪或者写就绪),能够通知程序进行相应的读写操作。
创新互联服务项目包括泰山网站建设、泰山网站制作、泰山网页制作以及泰山网络营销策划等。多年来,我们专注于互联网行业,利用自身积累的技术优势、行业经验、深度合作伙伴关系等,向广大中小型企业、政府机构等提供互联网行业的解决方案,泰山网站推广取得了明显的社会效益与经济效益。目前,我们服务的客户以成都为中心已经辐射到泰山省份的部分城市,未来相信会继续扩大服务区域并继续获得客户的支持与信任!
但select,poll,epoll本质上都是同步I/O ,因为他们都需要在读写事件就绪后自己负责进行读写,也就是说这个读写过程是阻塞的,而异步I/O则无需自己负责进行读写,异步I/O的实现会负责把数据从内核拷贝到用户空间。
I/O复用模型会用到select、poll、epoll函数:对一个IO端口,两次调用,两次返回,比阻塞IO并没有什么优越性。但关键是 能实现同时对多个IO端口进行监听。
这几个函数也会使进程阻塞,但是和阻塞I/O所不同的是,这几个函数 可以同时阻塞多个I/O操作。而且可以同时对多个读操作,多个写操作的I/O函数进行检测,直到有数据可读或可写时,才真正调用I/O操作函数。
当一个客户端连接上服务器时,服务器就将其连接的fd加入fd_set集合,等到这个连接准备好读或写的时候,就通知程序进行IO操作,与客户端进行数据通信。大部分Unix/Linux 都支持 select 函数,该函数用于探测多个文件描述符的状态变化。
(1) 创建所关注的事件的描述符集合(fd_set),对于一个描述符,可以关注其上面的读(read)、写(write)、异常(exception)事件,所以通常,要创建三个fd_set,一个用来收集关注读事件的描述符,一个用来收集关注写事件的描述符,另外一个用来收集关注异常事件的描述符集合。
(2)调用select()等待事件发生。这里需要注意的一点是,select的阻塞与是否设置非阻塞I/O是没有关系的。
(3) 轮询所有fd_set中的每一个fd,检查是否有相应的事件发生,如果有,就进行处理。
优点:
相比其他模型,使用 select() 的事件驱动模型只用单线程(进程)执行,占用资源少,不消耗太多 CPU,同时能够为多客户端提供服务。如果试图建立一个简单的事件驱动的服务器程序,这个模型有一定的参考价值。
缺点:
(1)每次调用select,都需要把fd集合从用户态拷贝到内核态,这个开销在fd很多时会很大!!!(复制大量句柄数据结构,产生巨大的开销 )。
(2)同时每次调用select都需要在内核遍历传递进来的所有fd,这个开销在fd很多时也很大!!!(消耗大量时间去轮询各个句柄,才能发现哪些句柄发生了事件)。
(3)单个进程能够监视的文件描述符的数量存在最大限制,32位机默认是1024。
(4)select的触发方式是水平触发,应用程序如果没有完成对一个已经就绪的文件描述符进行IO操作,那么之后每次select调用还是会将这些文件描述符通知进程。
(5)该模型将事件探测和事件响应夹杂在一起,一旦事件响应的执行体庞大,则对整个模型是灾难性的。
poll库是在linux2.1.23中引入的,windows平台不支持poll。poll本质上和select没有太大区别,都是先创建一个关注事件的描述符的集合,然后再去等待这些事件发生,然后再轮询描述符集合,检查有没有事件发生,如果有,就进行处理。因此,poll有着与select相似的处理流程:
(1)select需要为读、写、异常事件分别创建一个描述符集合,最后轮询的时候,需要分别轮询这三个集合。而poll只需要一个集合,在每个描述符对应的结构上分别设置读、写、异常事件,最后轮询的时候,可以同时检查三种事件。
(2)它没有最大连接数的限制,原因是它是基于链表来存储的。
(1)大量的fd的数组被整体复制于用户态和内核地址空间之间,而不管这样的复制是不是有意义。
(2)poll还有一个特点是“水平触发”,如果报告了fd后,没有被处理,那么下次poll时会再次报告该fd。
poll和select,它们的最大的问题就在于效率。它们的处理方式都是创建一个事件列表,然后把这个列表发给内核,返回的时候,再去轮询检查这个列表,这样在描述符比较多的应用中,效率就显得比较低下了。
epoll是一种比较好的做法,它把描述符列表交给内核,一旦有事件发生,内核把发生事件的描述符列表通知给进程,这样就避免了轮询整个描述符列表。
epoll支持水平触发和边缘触发,最大的特点在于边缘触发,它只告诉进程哪些fd刚刚变为就绪态,并且只会通知一次。还有一个特点是,epoll使用“事件”的就绪通知方式,通过epoll_ctl注册fd,一旦该fd就绪,内核就会采用类似callback的回调机制来激活该fd,epoll_wait便可以收到通知。
epoll与select和poll的调用接口上的不同:select和poll都只提供了一个函数——select或者poll函数。而epoll提供了三个函数,epoll_create,epoll_ctl和epoll_wait,epoll_create是创建一个epoll句柄;epoll_ctl是注册要监听的事件类型;epoll_wait则是等待事件的产生。
(1)创建一个epoll描述符,调用epoll_create()来完成。epoll_create()有一个整型的参数size,用来告诉内核,要创建一个有size个描述符的事件列表(集合)。
(2)给描述符设置所关注的事件,并把它添加到内核的事件列表中。这里需要调用epoll_ctl()来完成。
(3)等待内核通知事件发生,得到发生事件的描述符的结构列表。该过程由epoll_wait()完成。得到事件列表后,就可以进行事件处理了。
(1)没有最大并发连接的限制,能打开FD的上限远大于1024(1G的内存上能监听约10万个端口);
(2)效率提升。不是轮询的方式,不会随着FD数目的增加效率下降。只有活跃可用的FD才会调用callback函数;
即epoll最大的优点就在于它只管你“活跃”的连接,而跟连接总数无关,因此在实际的网络环境中,epoll的效率就会远远高于select和poll。
(3)内存拷贝。epoll通过内核和用户空间共享一块内存来实现消息传递的。利用mmap()文件映射内存加速与内核空间的消息传递;即epoll使用mmap 减少复制开销。epoll保证了每个fd在整个过程中只会拷贝一次(select,poll每次调用都要把fd集合从用户态往内核态拷贝一次)。
参考链接:
select、poll、epoll总结及ET、LT区别
main.c里面
#include stdio.h
#include stdlib.h
#include "fangkuai.h"
#include time.h
int main()
{
Manager manager;
Control control;
initGame(manager,control);
do {
printPrompting();
printPoolBorder();
runGame(manager,control);
if(ifPlayAgain()){
SetConsoleTextAttribute(Output,0x7);
system("cls");
startGame(manager,control);
}
else{
break;
}
}
while(1);
gotoxyFull(0,0);
CloseHandle(Output);
return 0;
}
.h里面
#ifndef FANGKUAI_H_INCLUDED
#define FANGKUAI_H_INCLUDED
#include stdio.h //标准输入输出
#include string.h //字符数组
#include stdlib.h //标准库
#include time.h //日期和时间
#include conio.h //控制台输入输出
#include windows.h // windows控制台
#include stdbool.h //标准布尔函数
//定义句柄,结构体,数组;函数声明
//定义方块数组,[7][4],7种方块,每种4个状态
static const unsigned int TetrisTable[7][4]={
{0x4444,0x0f00,0x2222,0x00f0},
{0x04e0,0x4640,0x0720,0x0262},
{0x0446,0x0e80,0x6220,0x0170},
{0x0622,0x02e0,0x4460,0x0740},
{0x0630,0x0264,0x0c60,0x2640},
{0x0462,0x06c0,0x4620,0x0360},
{0x0660,0x0660,0x0660,0x0660},
};
typedef struct TetrisManger{
unsigned int pool[28];
int x;int y;
int type[3];
int orientation[3];
unsigned score;
unsigned erasedCount[4];
unsigned erasedTotal;
unsigned tetrisCount[7];
unsigned tetrisTotal;
bool dead;
}Manager;
static const unsigned int initTetrisPool[28]={
0xc003,0xc003,0xc003,0xc003,0xc003,0xc003,0xc003,
0xc003,0xc003,0xc003,0xc003,0xc003,0xc003,0xc003,
0xc003,0xc003,0xc003,0xc003,0xc003,0xc003,0xc003,
0xc003,0xc003,0xc003,0xc003,0xc003,0xffff,0xffff
};
typedef struct TetresControl{
bool pause;
bool clockwise;
int direction;
int color[28][16];
}Control;
HANDLE Output;
void initGame(Manager *manager,Control *control);
void gotoxyFull(short x,short y);
void printPrompting();
void printPoolBorder();
void printScore(const Manager *manager);
void printNextTetres(const Manager *manager);
void startGame(Manager *manager,Control *control);
void initTetris(Manager *manager);
void insertTetris(Manager *manager);
void setPoolColor(const Manager *manager,Control *control);
void printCurrentTetris(const Manager *manager,const Control *control);
void printTetrisPool(const Manager *manager,const Control *control);
bool checkCollision(const Manager *manager);
void removeTetris(Manager *manager);
void moveDownTetris(Manager *manager,Control *control);
void runGame(Manager *manager,Control *control);
void horzMoveTetris(Manager *manager,Control *control);
void keydownControl(Manager *manager,Control *control,int key);
void rotateTetris(Manager *manager,Control *control);
void dropTetris(Manager *manager,Control *control);
bool checkErasing(Manager *manager,Control *control);
bool ifPlayAgain();
#endif // FANGKUAI_H_INCLUDED
.c里面
#include "fangkuai.h"
#include stdio.h
#include windows.h
void initGame(Manager *manager,Control *control)//初始化游戏
{
SetConsoleTitle("俄罗斯方块"); //设置窗口标题
Output=GetStdHandle(STD_OUTPUT_HANDLE); //获取标准输出句柄
CONSOLE_CURSOR_INFO INFO; //定义光标属性结构体变量
INFO.dwSize=1; //设置光标高度值
INFO.bVisible=FALSE; //设置光标隐藏值
SetConsoleCursorInfo(Output,INFO); //设置光标属性
startGame(manager,control);
}
//全角定位光标
void gotoxyFull(short x,short y) //全角方式定位
{
static COORD cd; //定义结构体变量
cd.X=2*x; //结构体变量 X=2x
cd.Y=y;
SetConsoleCursorPosition(Output,cd);//设置光标位置
}
//显示右下角按键提示信息
void printPrompting(){
SetConsoleTextAttribute(Output,0x0b);//设置显示颜色为蓝色光亮
gotoxyFull(26,10);
printf("■控制:");
gotoxyFull(27,12);
printf("□向左移动:← A 4");
gotoxyFull(27,13);
printf("□向右移动:→ D 6");
gotoxyFull(27,14);
printf("□向下移动:↓ S 2");
gotoxyFull(27,15);
printf("□顺时针转:↑ W 8");
gotoxyFull(27,16);
printf("□逆时针转:0");
gotoxyFull(27,17);
printf("□直接落地:空格");
gotoxyFull(27,18);
printf("□暂停游戏:回车");
gotoxyFull(26,23);
printf("■BY YU");
}
//显示游戏池白色边框
void printPoolBorder(){
SetConsoleTextAttribute(Output,0xF0);//设置背景色为白色高亮
int y=1;
for (y=1;y23;y++){
gotoxyFull(10,y); //(10,1) 定位到(10,22)
printf("%2s","");
gotoxyFull(23,y); //(23,1)定位到(23,22)
printf("%2s","");
}
gotoxyFull(10,23);//底部一行
printf("%28s",""); //14个字符,每个两位,左边、右边白色,中间12
}
//显示得分、消行数、方块数
void printScore(const Manager *manager){
SetConsoleTextAttribute(Output,0x0E);//设置颜色为黄色高亮
gotoxyFull(2,2);
printf("■得分:%u",manager-score);
gotoxyFull(1,6);
printf("■消行总数:%u",manager-erasedTotal);
int i;
for (i=0;i4;i++){
gotoxyFull(2,7+i);
printf("□消%d:%u",i+1,manager-erasedCount[i]);
}
gotoxyFull(1,15);
printf("■方块总数:%u",manager-tetrisTotal);
static const char *tetrisName="ITLJZSO";
for (i=0;i7;i++){
gotoxyFull(2,17+i);
printf("□%c形:%u",tetrisName[i],manager-tetrisCount[i]);
}
}
void printNextTetres(const Manager *manager)//显示下一个,下下一个方块
{
SetConsoleTextAttribute(Output,0x0f);//设置前景色为白色高亮
gotoxyFull(26,1);
printf("┌─────────┬─────────┐");
gotoxyFull(26,2);
printf("│%9s│%9s│","","");
gotoxyFull(26,3);
printf("│%9s│%9s│","","");
gotoxyFull(26,4);
printf("│%9s│%9s│","","");
gotoxyFull(26,5);
printf("│%9s│%9s│","","");
gotoxyFull(26,6);
printf("└─────────┴─────────┘");
//显示下一个方块
unsigned int tetris ;
int i;
tetris=TetrisTable[manager-type[1]][manager-orientation[1]];
SetConsoleTextAttribute(Output,manager-type[1]|8);
for (i=0;i16;i++){
gotoxyFull(27+(i3),2+(i2));
((tetrisi)0x8000) ? printf("■"):printf("%2s","");
}
tetris=TetrisTable[manager-type[2]][manager-type[2]];
SetConsoleTextAttribute(Output,0x08);
for(i=0;i16;i++){
gotoxyFull(32+(i3),2+(i2));
((tetrisi)0x8000)?printf("■"):printf("%2s","");
}
}
void startGame(Manager *manager,Control *control)//开始游戏
{
memset(manager,0,sizeof(Manager));
memcpy(manager-pool,initTetrisPool,sizeof(unsigned int [28]));//复制游戏池数据
srand((unsigned)time(NULL));//设置随机数种子
manager-type[1]=rand()%7;//下一个方块类型
manager-orientation[1]=rand()%4;//下一个方块状态
manager-type[2]=rand()%7;//下下一个方块类型
manager-orientation[2]=rand()%4;//下下一个方块状态
memset(control,0,sizeof(Control));//初始化控制结构体为0
initTetris(manager); //初始化方块
setPoolColor(manager,control);//初始化方块,若没有碰撞,插入方块,需要设置颜色
}
void initTetris(Manager *manager)//出第一个方块
{
unsigned int tetris;
manager-type[0]=manager-type[1];
manager-orientation[0]=manager-orientation[1];
manager-type[1]=manager-type[2];
manager-orientation[1]=manager-orientation[2];
manager-type[2]=rand()%7;
manager-orientation[2]=rand()%4;
tetris=TetrisTable[manager-type[0]][manager-orientation[0]];
manager-x=6;
manager-y=4;
if(checkCollision(manager)){
manager-dead=true;
}
else{
insertTetris(manager);
}
++manager-tetrisTotal;
++manager-tetrisCount[manager-type[0]];
printNextTetres(manager);
printScore(manager);
}
//插入方块到游戏池
void insertTetris(Manager *manager){
unsigned int tetris=TetrisTable[manager-type[0]][manager-orientation[0]];//相对于y的位置
manager-pool[manager-y+0]|=(((tetris0x0)0xF000)manager-x);//或等于
manager-pool[manager-y+1]|=(((tetris0x4)0xF000)manager-x);
manager-pool[manager-y+2]|=(((tetris0x8)0xF000)manager-x);
manager-pool[manager-y+3]|=(((tetris0xc)0xF000)manager-x);
}
//设置游戏颜色
void setPoolColor(const Manager *manager, Control *control){
int i,x,y;
/* i 当前方块值 0----15
x 设置颜色的列 x=manager-x+3
y 设置颜色的行 y=manager-y+i2 */
unsigned int tetris;
tetris=TetrisTable[manager-type[0]][manager-orientation[0]];
for(i=0;i16;i++){
y=manager-y+(i2);
if(y25){
break;
}
x=manager-x+(i3);
if((tetrisi)0x8000){
control-color[y][x]=(manager-type[0]|8);
}
}
}
//显示当前方块
void printCurrentTetris(const Manager *manager,const Control *control){
int x,y;
y=(manager-y4)?(manager-y-1):4;
for (;y26ymanager-y+4;y++){
x=(manager-x2)?(manager-x-1):2;
for (;x14xmanager-x+5;x++) {
gotoxyFull(x+9,y-3);
if((manager-pool[y]x) 0x8000){
SetConsoleTextAttribute(Output,control-color[y][x]);
printf("■");
}
else{
SetConsoleTextAttribute(Output,0);
printf("%2s","");
}
}
}
}
//显示游戏池
void printTetrisPool(const Manager *manager,const Control *control){
int x,y;
for(y=4;y26;y++)
{
gotoxyFull(11,y-3);
for(x=2;x14;x++)
{
if((manager-pool[y]x) 0x8000)
{
SetConsoleTextAttribute(Output,control-color[y][x]);
printf("■");
}
else {
SetConsoleTextAttribute(Output,0);
printf("%2s","");
}
}
}
}
//碰撞检测
bool checkCollision(const Manager *manager){
unsigned int tetris;
tetris=TetrisTable[manager-type[0]][manager-orientation[0]];//准备插入的当前方块
unsigned int dest;//游戏池中4行4列的值
dest=0;
dest|=(((manager-pool[manager-y+0]manager-x)0xF000)0x0);
dest|=(((manager-pool[manager-y+1]manager-x)0xF000)0x4);//左移x,右移4
dest|=(((manager-pool[manager-y+2]manager-x)0xF000)0x8);
dest|=(((manager-pool[manager-y+3]manager-x)0xF000)0xc);
return((desttetris)!=0);
}
//移除方块
void removeTetris(Manager *manager){
unsigned int tetris;
tetris=TetrisTable[manager-type[0]][manager-orientation[0]];//准备插入的当前方块,当前方块值
manager-pool[manager-y+0]=~(((tetris0x0)0xF000)manager-x);//左移0保留 最高,其余清零,右移x,游戏中方块列位置,取反,原来后面,存
manager-pool[manager-y+1]=~(((tetris0x4)0xF000)manager-x);
manager-pool[manager-y+2]=~(((tetris0x8)0xF000)manager-x);
manager-pool[manager-y+3]=~(((tetris0xc)0xF000)manager-x);
}
//向下移动方块
void moveDownTetris(Manager *manager,Control *control){
int y=manager-y;
removeTetris(manager);
++manager-y;
if(checkCollision(manager)){
manager-y=y;
insertTetris(manager);
if(checkErasing(manager,control))
{
printTetrisPool(manager,control);
}
}
else{
insertTetris(manager);
setPoolColor(manager,control);
printCurrentTetris(manager,control);
}
}
//运行游戏
void runGame(Manager *manager,Control *control){
clock_t clockNow,clockLast;
clockLast=clock();
printTetrisPool(manager,control);
while (!manager-dead){
while(_kbhit()){
keydownControl(manager,control,getch());
}
if (!control-pause){
clockNow=clock();
if(clockNow-clockLast0.45F*CLOCKS_PER_SEC){
clockLast=clockNow;
moveDownTetris(manager,control);
}
}
}
}
#includestdio.h
#includestdlib.h
#includetime.h
int main()
{
char pool[]=
{
'0','1','2','3','4','5','6','7','8','9',
'a','b','c','d','e','f','g','h','i','j',
'k','l','m','n','o','p','q','r','s','t',
'u','v','w','x','y','z','A','B','C','D',
'E','F','G','H','I','J','K','L','M','N',
'O','P','Q','R','S','T','U','V','W','X',
'Y','Z'
};//随机池
srand(time(0));
char pwd[9];
pwd[8]='\0';//方便作为字符串输出处理
int i=0;
while(i!=8)
{
pwd[i++]=pool[rand()%sizeof(pool)];
}
printf("密码%s",pwd);
}
{
push(s,a[n]);
}
for(n=i-i/2;ni;++n)
{
pop(s,x);
if(x!=a[n])
break;
}
if(print(s))
cout"字符串中心对称";
else
cout"字符串中心不对称";
}
故障现象
使用多线程的时候,主函数尚且还在执行,但子函数却不执行。
if __name__=='__main__':
print('Parent process %s.' % os.getpid())
p = Pool(processes = 4)
for i in range(30):
p.apply_async(func = stitch, args=(i,))
print('Waiting for all subprocesses done...')
p.close()
p.join()
print('All subprocesses done.')
1
2
3
4
5
6
7
8
9
1
2
3
4
5
6
7
8
9
如图所示,main会执行,但stitch就不会了。
原因分析
这是因为你的参数列表,也就是args和上面stitch函数的定义不一致。
解决方案
你只要把参数列表改为一致就可以了。
在C中,正常情况下,我们只能从函数中返回一个值。但在有些情况下,我们需要从函数中返回多个值,此时使用数组或指针能够很好地完成这样的任务。这里是一
个示例,这个程序使用一个整型数组作为参数,并将数组元素的和与积返回给调用函数。下面是C代码,这是一种常见情况。下面给出实现技巧:
1.
#include
2.
#include
3. int*
Pool(int array[],int size)
4.
{
5.
int *x;
6.
int i=0;
7.
int
a[2]={0,1};
8.
for(i=0;i
9.
{
10.
a[0]+=array[i];
// 存储数组元素值的和
11.
a[1]*=array[i];
// 存储数组元素值的积
12.
}
13. //
将数组的基地址赋值给整型指针
14.
x=a[0];
15. //
返回整个数组
16.
return x;
17. }
18.
19. int main()
20. {
21. int
a[]={1,2,3,4};
22. int
*c; c = Pool(a,4);
23.
printf("Sum = %d\nProduct = %d\n",c[0],c[1]);
24.
getch();
25.
return 0;
26. }
这样,我们就知道如何使用数组和指针从C函数中返回多个值。在很多情况下你会发现这个技巧很有用。
售后响应及时
7×24小时客服热线数据备份
更安全、更高效、更稳定价格公道精准
项目经理精准报价不弄虚作假合作无风险
重合同讲信誉,无效全额退款