收藏 分享(赏)

05并发服务器.doc

上传人:fmgc7290 文档编号:8130977 上传时间:2019-06-10 格式:DOC 页数:25 大小:256.50KB
下载 相关 举报
05并发服务器.doc_第1页
第1页 / 共25页
05并发服务器.doc_第2页
第2页 / 共25页
05并发服务器.doc_第3页
第3页 / 共25页
05并发服务器.doc_第4页
第4页 / 共25页
05并发服务器.doc_第5页
第5页 / 共25页
点击查看更多>>
资源描述

1、教案教学专题 授课学时教学章节 授课对象教学类型 授课形式教学重点教学难点学习要求知识点了解 理解 掌握 熟练掌握服务器的分类多进程并发服务器的实现多线程并发服务器的实现教学内容和教学目标线程安全函数教学过程教学提示媒体使用课后导读教学后记讲稿教学内容 教学设计5.1 服务器的分类按连接类型分类面向连接的服务器(如tcp)面向无连接的服务器(如udp)按处理方式分类迭代服务器并发服务器教学内容 教学设计Linux系统主要提供3种方式支持并发:进程、线程及I/O多路复用。5.2 多进程并发服务器5.2.1 进程基础进程定义了一个计算的基本单元,可以认为是一个程序的一次运行。它是一个动态实体,是独

2、立的任务。 进程是独立的,未经允许一个进程不能访问另一个进程的资源,它拥有独立的地址空间、 执行堆栈、文件描述符等。每个进程拥有独立的地址空间,进程间正常情况下,互不影响,一个 进程的崩溃不会造成其他进程的崩溃。当进程间共享某一资源时,需注意两个问题:同步问题和通信问题。进程又是相互影响的,进程之间可以通过IPC 机制相互通信,IPC(Inter-Process Communication,进程间通信)http:/ 统可以通过分时处理的方式使计算机各个进程分别在不同时间段执行,实现“同时”执行。linux下进程间通信的几种主要手段简介:1. 管道(Pipe)及有名管道(named pipe):

3、管道可用于具有亲缘关系进程间的通信,有名管道克服了管道没有名字的限制,因此,除具有管道所具有的功能外,它还允许 无亲缘关系进程间的通信;2. 信号(Signal):信号是比较复杂的通信方式,用于通知接受进程有某种事件发生,除了用于进程间通信外,进程还可以 发送信号给进 程本身;linux除了支持Unix早期信号语义函数sigal 外,还支持语扩展:进程间通信教学内容 教学设计义符合Posix.1标准的信号函数 sigaction(实际上, 该函数是基于BSD的, BSD为了实现可靠信号机制,又能够统 一对外接口,用sigaction函数重新实现了signal函数);3. 报文(Message)

4、队列(消息队列):消息队列是消息的链接表,包括Posix 消息队列system V消息队列。有足 够权限的进程可以向 队列中添加消息,被 赋予读权 限的进程则可以读走队列中的消息。消息队列克服了信号承 载信息量少,管道只能承载无格式字节 流以及缓冲区大小受限等缺点。4. 共享内存:使得多个进程可以访问同一块内存空间,是最快的可用IPC形式。是 针对其他通信机制运行效率较低而设计的。往往与其它通信机制,如信号量结合使用,来达到进程间的同步及互斥。5. 信号量(semaphore):主要作为进 程间以及同一进程不同线程之间的同步手段。6. 套接口(Socket ):更 为一般的进程间通信机制,可用

5、于不同机器之 间的进程间通信。起初是由 Unix系统的BSD分支开 发出来的,但现在一般可以移植到其它类Unix系统上:Linux 和System V的变种都支持套接字。5.2.2 进程创建可以通过fork 或vfork 函数来 创建新进程。在 创建新进程时,要进行资源拷贝。Linux有3种 资源拷贝的方式:1)共享:新老进程共享通用的资源。当共享资源时,两个进程共用一个数据结构,不需要为新进程另建。2)直接拷贝:将父进程的文件、文件系统、虚 拟内存等结构直接拷贝到子进程中。子进程创建后,父子进程拥有相同的结构。3)Copy on write:拷 贝虚 拟内存页是相当困 难和耗时的工作,所以不

6、能拷贝就最好不拷贝,如果必须拷贝,也要尽可能地少拷贝。为此, Linux采用可Copy on write技术,把真正的虚拟内存拷贝推迟到两个进程中的任一个试图写虚拟页时。#include #include pid_t fork(void)返回:父进程中返回子进程的进程ID, 子进程返回0,-1出错fork后,子进程和父进程继续执行fork()函数后的指令。子进程是父进程的副本。子进程拥有父进程的数据空间、堆 栈的副本。但父、子进程并不共享这些存储空间部分。如果代码段是只读的, 则父子进程共享代码段。 如果父子进程同时对同一文件描述字操作,而又没有任何形式的同步,则会出现混乱的状况;父进程中调用

7、fork之前打开的所有描述字在函数fork返回之后子进程会得到一个副本。fork 后,父子进 程均需要将自己不使用的描述字关 闭。fork()函数采用Copy on write方式!系统给每个进程定义了一个标识该进程的非负正数,称作进程标识符。当某一 进程终止后,其标识符可以重新用作另一进程的标识符。不过,在任何 时刻,一个标识符所代表的进程是唯一的。系统把标识符 0 和 1 保留给系统的两个重要进程。进程 0 是调度进程,它按一定的原则把处 理机分配给进程使用。 进程 1 是初始化 进程,它是程序/sbin/init 的执行。进程 1 是 UNIX 系统那其它 进程的祖先,并且是 进程结构的

8、最终控制者。扩展:fork的返回值教学内容 教学设计fork示例pid_t pid;if(pid = fork() 0)printf(“parent process is runingn“);。 else if(pid = 0)printf(“child process is runingn“);sleep(5);printf(“child process is runing exitn“);exit(0);详细讲解如果fork 调用成功,该函数调用一次会返回两次。在调用进程(父进程)返回一次,返回值是新派生的子进程ID号;而在子进 程中它还返回一次,返回值是0,因此可以通过返回值来区别当前进

9、程是子进程还是父进程。为什么子进程返回0而不是父进程id?子进程只有一个父进程,可以通过geippid函数来获得父进程id,而父进程无法通过一个函数获得所有子进程的id。fork后是父 进程还是子进程先执行没有规律,根据系统的不同而不同。Fork的典型应用:(1)父子进程执 行不同的程序段,非常典型的网络服务器。父进程等待客户的服务请求。请求到达,父进程fork 子进程,有子 进程进行处理,父 进程继续等待,同时父子进程各自关闭不需要的描述符。(2)每个进程要 执行不同的程序,子进程从fork函数返回后立即调用exec函数执行其他程序。getpid 和 getppid利用系统调用 getpid

10、 可以得到程序本身的进程标识 符,其用法如下:pid=getpid();利用系统调用 getppid 可以得到调用进程的父进程的 标识符,其用法如下:ppid=getppid();#include #include pid_t vfork(void);该系统调用基本上与fork 相同,在BSD3.0中开始出现,主要为了解决fork昂贵的开销。两者的基本区别在于当使用vfork() 创建新进程时,父进程将被暂时阻塞,而子进程则可以借用父进程的地址空间,直到子进程退出或调用execve()函数,至此父进程才继续执行。fork测试/*提问:如何获得进程号?教学内容 教学设计* fork_test_m

11、ain.c* Created on: 2010-6-13* Author: kerwin* fork 子进程进行完全内存拷贝,子进程拥有与父进程相同的资源,父子进程共享CPU。*/#include #include #include#includeint main(int argc, char* argv)printf(“main sub is running!n“);sleep(2);pid_t pid;if(pid = fork() 0)printf(“parent process is runing, pid:%dn“, getpid();int chdstatus = 0;pid_t

12、tmpid = 0;sleep(8);printf(“nothing todon“); else if(pid = 0)printf(“child process is runing, pid:%d ppid:%dn“, getpid(), getppid();sleep(5);printf(“child process is runing exitn“);exit(0); else printf(“fork() error!n“);exit(0);while(1);printf(“parent process is runing exitn“);return 0;5.2.3 进程终止进程的终

13、止存在两个可能:父进程先于子进程终止(init进程领养)子进程先于主进程终止对于后者,系统内核为子进程保留一定的状态信息:进程ID 、终止状态、CPU时间等;当父进程调用wait或waitpid函数时,获取这些信息;之后系统内核释放其存储空间、关闭其打开文件,一个已经终止但父进程尚未对其进行善后处理的进程称为“ 僵尸进程 ”。僵尸进程:一个进程在调用exit命令结束自己的生命的时候,其实它并没有真正的被 销毁, 提问:具体解释 僵尸进程及其危害教学内容 教学设计而是留下一个称为僵尸进程(Zombie)的数据结构(系统调用exit,它的作用是 使进程退出,但也 仅仅限于将一个正常的 进程变成一个

14、僵尸进程,并不能将其完全销毁)僵尸进程危害:UNIX提供了一种机制可以保证只要父进程想知道子进程结束时的状态信息, 就可以得到. 这种机制就是: 在每个进程退出的时候,内核释放该进程所有的资源,包括打开的文件,占用的内存等. 但是仍然为其保留一定的信息(包括 进程号the process ID,退出状态the termination status of the process,运行 时间the amount of CPU time taken by the process等), 直到父进程通过wait / waitpid来取 时才释放. 但这样就导致了问题,如果你进程不调用wait / wai

15、tpid的话, 那么保留的那段信息就不会 释放,其进程号就会一定被占用,但是系统所能使用的进程号是有限的,如果大量的产生 僵死进程,将因为没有可用的进程号而导致系统不能产生新的进程. 此即为僵尸进程的危害,应当避免.僵尸进程的处理: 它需要它的父进程来为它收尸,如果他的父 进程没安装SIGCHLD信号处理函数调用wait或waitpid()等待子进程结束,又没有显式忽略该信号,那么它就一直保持僵尸状态; 如果这时父进程结束了,那么 init进程自动会接手这个子进程,为它收尸,它还是能被清除的。子进程结束后为什么要进入僵尸状态? 因为父进程可能要取得子进程的退出状态等信息。 僵尸状态是每个子进程

16、比经过的状态吗? 是的。 任何一个子进程(init除外)在exit()之后,并非马上就消失掉,而是留下一个称为僵尸进程(Zombie)的数据结构,等待父 进程处理。 这是每个 子进程在结束时都要经过的阶段。如果子进程在exit() 之后,父进程没有来得及处理,这时用ps命令就能看到子进程的状态是“Z”。如果父进程能及时 处理,可能用ps命令就来不及看到子进程的僵尸状态。$ ps -el 其中,有标记为Z的进程就是僵尸进程 S代表休眠状态;D 代表不可中断的休眠状态;R 代表运行状态;Z代表僵死状 态;T代表停止或跟踪状态if(pid 0)printf(“parent is running: %

17、dn“, getpid();else if(pid = 0)printf(“children is running: %dn“, getpid();exit(1);while(1) /子进程exit() 父进程不wait() 这时在ps中可以看到僵尸 进程进程终止函数#include void exit(int status);本函数终止调用进程。关闭所有子进程打开的描述符,向父进程发送SIGCHLD信号,并返回状 态。exit()除了停止进程的运行外,它还有一些其它作用,其中最重要的是,它将关闭所有打开的文件。如果父进程因执行了 wait()调用而处于睡眠状态 ,那么子进程执行 exit()

18、会新启动父进程运行。另外, exit()还将完成一些系统内部的清除工作,例如缓冲区的清除作等。其他程序停止情况:除了使用 exit()来终止进程外,当进程运行完其程序到达 main()函数末时,进程会自动终止。教学内容 教学设计当进程在 main()函数内执行一个 return语句时,它也会终止。#include #include pid_t wait(int *stat_loc);返回:终止子进程的ID成功;-1出错;stat_loc 存储子进程的终止状态(一个整数);关于wait的几种情况如果没有终止的子进程,但是有一个或多个正在执行的子进程,则该函数将堵塞,直到有一个子进程终止或者wai

19、t被信号中断时,wait返回,等待的父进程就会重新执行。当调用该系统调用时,如果有一个子进程已经终止,则该系统调用立即返回,并释放子进程所有资源。如果既没有正在执行的子进程,又没有终止的子进程,则返回-1.wait()和 exit()的配合使用:用 wait()和 exit()联用来等待子进程终止的情况。 还有两种进程 终止情况值得讨论。 这两种情况为:1子进程终止时,父进程并不正在执行 wait()调用。2当子进程尚未终止时,父进程却终止了。在第一种情况中,要终止的进程就处于一种过渡状态(称为 zombie) ,处于这种状态的进程不使用任何内核资源,但是要占用内核中的进程处理表那的一项。当其

20、父进程执行wait()等待子进程时,它会进入睡眠状态,然后把这种处于过渡状态的进程从系统内删除,父进程仍将能得到该子进程的结束状态。在第二种情况中,一般允许父进程结束,并把它的子进程(包括处于过渡状态的进程)交归系统的初始化进程所属。使用wait()函数可能会出 现一个问题由于linux信号不排队,在SIGCHLD信号同时到来后,信号 处理程序中调用了wait函数,其只 执行一次,这样将留下2 个僵尸进程。可以使用waitpid函数解决这个问题。注意:wait()使用上的问题教学内容 教学设计pid_t waitpid(pid_t pid, int *stat_loc, int options

21、);返回:终止子进程的ID成功;-1出错;stat_loc 存储子进程的终止状态;当pid=-1,option=0时 ,该函数等同于wait,否则由参数pid和 option共同决定函数行为,其中pid参数意义如下:-1:要求知道任何一个子进程的返回状态(等待第一个终止的子进程);0:要求知道进程号为pid 的子进程的状态;#include #include#includeint main(int argc, char* argv)printf(“main sub is running!n“);sleep(2);pid_t pid;if(pid = fork() 0)printf(“paren

22、t process is runing, pid:%dn“, getpid();int chdstatus = 0;pid_t tmpid = 0;/使用waitpid方式while(1)tmpid = waitpid(pid, if(tmpid = pid)printf(“child %d terminated %dn“, tmpid, chdstatus);else/no child terminated;continue; else if(pid = 0)printf(“child process is runing, pid:%d ppid:%dn“, getpid(), getppi

23、d();教学内容 教学设计sleep(5);printf(“child process is runing exitn“);exit(0); else printf(“fork() error!n“);exit(0);return 0;5.2.4 多进程并发服务器多进程并发服务器状态图5.2.5 多进程并发服务器实例教学内容 教学设计多进程实例:服务器等待接收客户的连接请求,一旦连接成功则显示客户地址,接着接收客户端的名称并显示;然后接收来自该客户的字符串,每当收到一个字符串时,显示该字符串,并将字符串按照恺撒密码的加密方式(K=3 )进行加密,再将加密后的字符发回客户端;之后, 继续等待接收

24、该客户的信息,直到客户关闭连接。要求服务器具有同时处理多个客户请求的能力。multiprocess_s_main.c/* multiprocess_s_main.c教学内容 教学设计* Created on: 2010-7-7* Author: kerwin*/#include #include #include #include #include #include #include #include #include #define PORT 1234#define BACKLOG 5#define MAXDATASIZE 1000void process_cli(int connfd, s

25、truct sockaddr_in client);int main(int argc, char* argv)int listenfd, connfd;pid_t pid;struct sockaddr_in server;struct sockaddr_in client;int len;if (listenfd = socket(AF_INET, SOCK_STREAM, 0) = -1)/AF_INET IPV4协议perror(“create scoket failed.n“);exit(1);int opt = SO_REUSEADDR;setsockopt(listenfd, S

26、OL_SOCKET, SO_REUSEADDR, bzero(server.sin_family = AF_INET;server.sin_port = htons(PORT);server.sin_addr.s_addr = htonl(INADDR_ANY);if(bind(listenfd, (struct sockaddr *)exit(1);if(listen(listenfd, BACKLOG) = -1)perror(“listen() error./n“);exit(1);len = sizeof(client);while(1)教学内容 教学设计if(connfd = acc

27、ept(listenfd, (struct socketaddr*)exit(1);int waitsta;wait(if(pid = fork() 0)/主进程close(connfd);continue; else if(pid = 0)close(listenfd);process_cli(connfd, client);exit(0); else perror(“fork() error./n“);exit(1);close(listenfd);return 0;void process_cli(int connfd, struct sockaddr_in client)int num

28、;char recvbufMAXDATASIZE, sendbufMAXDATASIZE, cli_nameMAXDATASIZE;printf(“You got a conntion from %s. n“, inet_ntoa(client.sin_addr);num = recv(connfd, cli_name, MAXDATASIZE, 0);if(num = 0)close(connfd);printf(“Client disconnected.n“);return;cli_namenum - 1 = 0;printf(“Clients name is %s. n“, cli_na

29、me);while(num = recv(connfd, recvbuf, MAXDATASIZE, 0)recvbufnum = 0;printf(“Received client ( %s ) message: %s“, cli_name, recvbuf);int i = 0;for(i = 0; i = a 提问:多进程服务器没有问题么?教学内容 教学设计sendbufi = recvbufi;sendbufnum -1 = 0;send(connfd, sendbuf, strlen(sendbuf), 0);close(connfd);5.3 多线程服务器多进程服务器的问题虽然多进

30、程并发服务器模式很多年来都使用得很好,但使用 fork生成子进程存在一些问题。首先,fork占用大量的资源,内存映像要从父进程拷贝到子进程,所有描述符要在子进程中复制等。虽然当前采用写 时拷贝(copy-on-write)技 术,将真正的拷贝推迟到子进程有写操作时,但 fork仍然需要占用大量资源。其次,fork子进程后,需要用进程间通信(IPC )在父子进程间传递信息。由于子 进程从一开始就有父进程数据空间及所有描述符的拷贝,所以 fork之前的信息容易传递,但是从子进程返回信息给父进程就需要做很多工作。 5.3.1 线程基础线程是进程内的独立执行实体和调度单元,又称为“ 轻量级”进程(li

31、ghtwight process);创建线程比进程快10100 倍。一个进程内的所有线程共享相同的内存空间、全局变量等信息(这种机制又带来了同步问题)。而且它们还共享以下信息:共享信息 私有信息进程指令 线程ID大多数数据 寄存器集合(包括程序计数器和栈指针)打开的文件描述字 栈(用于存放局部变量)信号处理程序和信号处置 error当前工作目录 信号掩码用户ID和组ID 优先级5.3.2 线程基础函数#include int pthread_create(pthread_t *tid, const pthread_attr_t *attr, void *(*func)(void *), voi

32、d *arg);返回:成功时为0;出错时为正的Exxx值当一个程序开始运行时,系统会创建一个初始线程或主线程的单个线程。额外线程由上述函数创建;教学内容 教学设计新线程由线程id标识:tid,新线程的属性attr包括:优先级、初始栈大小、是否应该是守护线程等等。线程的执行函数和调用参数分别是:func和arg ;由于线程的执行函数的参数和返回值类型均为void *,因此可传递和返回指向任何类型的指针;常见的返回错误值:EAGAIN:超过 了系统线程数目的限制。ENOMEN:没有足够的内存产生新的线程。EINVAL:无效的属性attr值。#inlcude int pthread_join(pth

33、read_t tid, void *status);返回:成功时为0;出错时为正的Exxx值,不设置error该函数类似与waitpid函数,但必 须指定等待线程的ID,该函数不能等待任意一个线程结束(如wait);被等待线程必须是当前进程的成员,并且不是分离的线程和守护线程。几个线程不能同时等待一个线程完成,如果一个线程成功调用pthread_join函数,其他线程就会返回 ESRCH错误。pthread_t pthread_self(void);返回:调用线程的线程id;线程或者是可联合的(joinable )(默认),或者是 脱离的(detached)。当可汇合的线程终止时,其线程id和

34、退出状态将保留,直到另外一个线程调用pthread_join。脱离的线程则像守护进程,当它终止时,释放所有资源,我 们不能等待它终止。#include int pthread_detach(pthread_t tid)返回:成功时为0;出错时为正Exxx值;pthread_detach(pthread_self();该函数将指定的线程变为分离的。#include void pthread_exit(void *status);无返回值;如果线程为可汇合的,将保留线程id和退出状态 供pthread_join()函数调用;指针status:指向线程的退出状态。不能指向一个局部变量,因为线程终止时

35、其所有的局部变量将被撤销;注意:exit()和pthread_exit()编程注意:lpthread教学内容 教学设计还有其他两种方法可使线程终止: 启动线程的函数(pthread_create的第3个参数)返回。其返回值便是线程的终止状态; 如果进程的main函数返回,或者当前 进程中,任一线程调用了exit()函数,将终止该进程中所有线程。5.3.3 给新线程传递函数线程编程的一般框架void *function(void *arg);int main(void) ARG * arg;int connfd;loop if(connfd = accept(sockfd,NULL,NULL)=

36、 -1) arg = (struct ARG*)malloc(sizeof(struct ARG);arg - connfd = connfd; /要创建线程时在构造参数指针pthread_create(void *function(void *arg) struct ARG info;Info.connfd = (struct ARG*)arg)-connfd;Info.other = (struct ARG*)arg)-other;close(info.connfd);free(arg);gcc -D -REENTRANT -lpthread xxx. C我们需要包含文件pthread.h

37、,并且使用 -lpthread来链接线程库。5.3.4 多线程并发服务器实例multithread_unsafe_s_main.c/* multithread_unsafe_s_main.c 5.4* Created on: 2010-9-3* Author: kerwin教学内容 教学设计*/#include #include #include #include #include #include #include #include #include #define PORT 1234#define BACKLOG 5#define MAXDATASIZE 1000void process_

38、cli(int connfd, struct sockaddr_in client);void savedata(char* recvbuf, int len, char* cli_data);void* function(void* arg);struct ARG int connfd;struct sockaddr_in client;int main(int argc, char* argv) int listenfd, connfd;pthread_t tid;struct ARG *arg;struct sockaddr_in server;struct sockaddr_in cl

39、ient;socklen_t len;/创建listenfdif (listenfd = socket(AF_INET, SOCK_STREAM, 0) = -1) perror(“Creating socket failed.“);exit(1);/设置socket选项int opt = SO_REUSEADDR;setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, /设置并绑定listenfdbzero(server.sin_family = AF_INET;server.sin_port = htons(PORT);server.sin_addr.

40、s_addr = htonl(INADDR_ANY);if (bind(listenfd, (struct sockaddr *) 教学内容 教学设计exit(1);if (listen(listenfd, BACKLOG) = -1) perror(“listen() errorn“);exit(1);len = sizeof(client);while (1) if (connfd = accept(listenfd, (struct sockaddr *) exit(1);/设置线程的参数arg = (struct ARG *) malloc(sizeof(struct ARG);arg

41、-connfd = connfd;memcpy(void *) /创建线程if (pthread_create(exit(1);close(listenfd);void process_cli(int connfd, struct sockaddr_in client) int num;char cli_data1000;memset(cli_data, 0, sizeof(cli_data);char recvbufMAXDATASIZE, sendbufMAXDATASIZE, cli_nameMAXDATASIZE;printf(“You got a connection from %s

42、.n“, inet_ntoa(client.sin_addr);num = recv(connfd, cli_name, MAXDATASIZE, 0);if (num = 0) close(connfd);printf(“Client disconnected.n“);return;cli_namenum - 1 = 0;printf(“Clients name is %s.n“, cli_name);while (num = recv(connfd, recvbuf, MAXDATASIZE, 0) recvbufnum = 0;printf(“Received client( %s )

43、message: %s“, cli_name, recvbuf);savedata(recvbuf, num, cli_data);教学内容 教学设计/设置回应信息int i;for (i = 0; i = a sendbufi = recvbufi;sendbufnum - 1 = 0;send(connfd, sendbuf, strlen(sendbuf), 0);close(connfd);printf(“Client( %s ) closed connection. nUsers data: %sn“, cli_name,cli_data);void* function(void*

44、arg) struct ARG *info;info = (struct ARG *) arg;process_cli(info-connfd, info-client);free(arg);pthread_exit(NULL);void savedata(char* recvbuf, int len, char* cli_data) static int index = 0;int i = 0;while (i int pthread_key_create(pthread_key_t *key, void (* destructor)(void *value);返回值:正常执行后返回0,否则

45、返回错误码该函数在进程内部分配一个标志TSD的关键字,关键字是进程内部唯一的,所有线程在创建时关键字值是NULL。Key指向创建的关 键字;destructor是一个可选的析构函数,用于每个线程终止时调用该析构函数。#include int pthread_setspecific(pthread_key_t key, const void *value);返回值:正常执行后返回0;否则返回正的错误码该函数为TSD关键字绑定一个与本线程相关的值;void * pthread_getspecific(pthread_key_t key);返回值:正常执行后返回与调用线程相关的关键字所绑定的值,否则

46、返回NULL。该函数获得与调用线程相关的关键字所绑定的值。int pthread_key_delete(pthread_key_t key);返回值:成功为0,否则为非0;该函数删除进程内的TSD表示的关键字。 该函数既不检查TSD 是否有绑定值,也不调用该关键字的析构函数。教学内容 教学设计include int pthread_once(pthread_once_t *once_control, void (*init_routine) (void);成功返回0,否则返回错误码 如果本函数中,once_control变量使用的初值为PTHREAD_ONCE_INIT,可保证init_rou

47、tine()函数在本 进程执行序列中仅执行一次。一般在init_routine 函数中完成一些初始化工作。5.3.6 线程安全实例 尽管TSD实现略微有些复杂,但却是将一个非线程安全函数转化为线程安全常用的方法。multithread_safe_s_main.c/* multithread_safe_TSD_s_main.c 5.5* with TSD instead of static variable* Created on: 2010-8-7* Author: kerwin*/#include #include #include #include #include #include #d

48、efine PORT 1234#define BACKLOG 5#define MAXDATASIZE 1000void process_cli(int connfd, struct sockaddr_in client);void savedata_r(char* recvbuf, int len, char* cli_data);void* function(void* arg);struct ARG int connfd;struct sockaddr_in client;pthread_key_t key;pthread_once_t once = PTHREAD_ONCE_INIT;static void destructor(void *ptr) free(ptr);教学内容 教学设计static void creatkey_once(void) printf(“create keyn“);pthread_key_create(struct STD_DATA int index;int main(int argc, char* argv) int listenfd, connfd;pthread_t tid;struct ARG* arg;struc

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 企业管理 > 管理学资料

本站链接:文库   一言   我酷   合作


客服QQ:2549714901微博号:道客多多官方知乎号:道客多多

经营许可证编号: 粤ICP备2021046453号世界地图

道客多多©版权所有2020-2025营业执照举报