赞
踩
多线程知识
Socket编程
while(1){ //死循环运行服务端
cfd=accept() //阻塞监听客户端的连接请求,接收客户端的连接
pthread_create() //创建线程,子线程去执行与客户端的通信
pthread_detach() //实现线程分离,回收子线程
//close(cfd) 不可以关闭cfd,这关了子线程的也没了
}
while(1){
//close(lfd) 不可以关闭lfd,主线程还要用
read() //读客户端数据
//处理数据
write() //写回数据
pthread_exit() //线程退出,可以指定返回值
}
1 为什么设置线程分离?
主线程与子线程分离,子线程结束后,资源自动回收,防止僵尸线程。
2 为什么是pthread_detach(),而不是pthread_join()?
使用pthread_create创建的线程有两种状态:joinable和unjoinable。默认是joinable 状态。
在服务器中,当主线程监听并连接到新的客户端,创建子线程通过cfd与客户端通信时,主线程并不希望因为调用pthread_join而阻塞,因为主线程还要继续去执行阻塞监听。你pthread_join把我阻塞在这里算怎么回事。
3 pthread_join和pthread_detach()的应用场景?
pthread_detach()和pthread_join()就是控制子线程回收资源的两种不同的方式。
pthread_join()函数是一个阻塞函数,调用方会阻塞到pthread_join所指定的tid的线程结束后才被回收 ,一般应用在主线程需要等待子线程结束后才继续执行的场景。(子线程合入主线程,主线程会一直阻塞,直到子线程执行结束,然后回收子线程资源,并继续执行。)
pthread_detach()函数不会阻塞,调用它后,使得主线程与子线程分离,两者相互不干涉,子线程结束同时子线程的资源自动回收释放资源,非常方便。
4 设置线程分离的3种方式?
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
pthread_create(&tid, &attr, childthread_work, (void *)cfd);
也就是说,添加在do_work函数的第一行。
pthread_detach(pthread_self());
pthread_t tid;
ret= pthread_create(&tid,NULL,childthread_work, (void *)cfd);
if(ret==-1){
sys_err("pthread_create error");
}
pthread_detach(tid); //非阻塞,可立即返回
5 设置线程分离的3种方式?
由于pthread库不是Linux系统默认的库,连接时需要使用库libpthread.a,所以在使用pthread_create创建线程时,在编译中要加-lpthread。
比如:gcc multhreadserver.c wrap.c -o multhreadserver -lpthread
6 pthread_exit()和return的区别?
return 的含义是返回,它不仅可以用于线程执行的函数,普通函数也可以使用;pthread_exit() 函数的含义是线程退出,它专门用于结束某个线程的执行。
在子线程中,当执行结束, return 和 pthread_exit() 都可以给返回值到主线程,主线程中的 pthread_join() 函数都可以接收到线程的返回值。
7 要想让子线程总能完整执行(不会中途退出)的三种方法?
8 多线程错误返回值分析
所有线程的错误号返回都只能使用strerror这个函数判断,不能使用perror,因为perror是调用进程的全局错误号,不适合单独线程的错误分析,所以只能使用strerror。
比如:fprintf(stderr, “xxx error: %s\n”, strerror(ret));
9 pthreat_create()参数传递?
while(1){
cfd= Accept(lfd,(struct sockaddr*)&clt_addr,&clt_addr_len);
ret= pthread_create(&tid,NULL, childthread_work,(void *)cfd);
}
for (int i=0; i<10; ++i)
{
int *p = malloc(sizeof(*p));
*p = i;
if ((ret=pthread_create(&pid[i],NULL,thread,(void*)p)) != 0)
{
fprintf(stderr,"pthread_create:%s\n",strerror(ret));
exit(1);
}
}
总之: 不能在线程创建过程中,改变传递的参数,避免该问题产生的方法是传递值或者使用动态申请内存的方法。
10 为什么主线程不可以关闭cfd,子线程不可以关闭lfd?
同一进程间的线程具有共享和独立的资源,
其中共享的资源有进程代码段、进程的公有数据(利用这些数据,线程很容易实现相互之间的通讯),进程的所拥有资源。详细说:
0、进程代码段
1、进程申请的堆内存
2、进程打开的文件描述符
3、进程的全局数据(可用于线程之间通信)
4、进程ID、进程组ID
5、进程目录
6、信号处理器。
而独占资源有:
1、线程ID
2、寄存器组的值
3、线程堆栈
4、错误返回码
5、信号屏蔽码
6、线程的优先级
因为多线程共享进程打开的文件描述符,与多进程对比,主线程是不可以关闭cfd的,因为子线程并没有把fd表复制过来。如果关闭了cfd,就相当于释放掉了套接字,那么后续子线程就不能进行读写了。同理,lfd也是这样。
提示:多返回值传出的方式,来自ChernoCppTutorial的笔记:1、传引用或者指针,即函数设置多个传出参数。2、直接返回一个数组。当然这不通用,因为必须要同一种类型。当然还能写为vector,不过array会在栈上创建,而vector会把它的底层存储在堆上,所以从技术上来讲返回std::array会更快。3、tuple或pair。4、定义一个结构体,然后返回。
//server.c,需要和wrap.c一起gcc // Created on 2022/5/22. // #include <string.h> #include <strings.h> #include<netinet/in.h> #include <arpa/inet.h> #include <ctype.h> #include <pthread.h> #include <signal.h> #include <sys/wait.h> #include <fcntl.h> #include "wrap.h" #define IP "127.44.44.44" #define PORT 6266 struct s_info { //定义一个结构体, 将地址结构跟 cfd 捆绑 struct sockaddr_in cliaddr; int connfd; }; void *do_work(void *s_in) { struct s_info *client_info = (struct s_info *) s_in; int cfd = (*client_info).connfd; int ret; char buf[BUFSIZ], clie_ip[BUFSIZ]; while (1) { ret = read(cfd, buf, sizeof(buf)); if (ret == 0) { close(cfd); break; } else if (ret == -1) { sys_err("read error"); } else { printf("received from %s at PORT %d\n", inet_ntop(AF_INET, &(*client_info).cliaddr.sin_addr.s_addr, clie_ip, sizeof(clie_ip)), ntohs((*client_info).cliaddr.sin_port)); write(STDOUT_FILENO, buf, ret); for (int i = 0; i < ret; i++) { buf[i] = toupper(buf[i]); } write(cfd, buf, ret); } } close(cfd); return (void*)0; } int main(int argc,char *argv[]){ int lfd,cfd; int i=0; int ret; char buf[BUFSIZ]; pthread_t tid; struct sockaddr_in srv_addr,clt_addr; socklen_t clt_addr_len; struct s_info s_info_array[256]; //memset(&saddr,0,sizeof(saddr)); bzero(&srv_addr,sizeof(srv_addr)); srv_addr.sin_family=AF_INET; srv_addr.sin_port= htons(PORT); srv_addr.sin_addr.s_addr= htonl(INADDR_ANY); lfd= Socket(AF_INET,SOCK_STREAM,0); Bind(lfd,(struct sockaddr *)&srv_addr, sizeof(srv_addr)); Listen(lfd,128); while(1){ clt_addr_len = sizeof(clt_addr_len); cfd= Accept(lfd,(struct sockaddr*)&clt_addr,&clt_addr_len); s_info_array[i].cliaddr = clt_addr; s_info_array[i].connfd = cfd; ret= pthread_create(&tid,NULL,do_work,(void *)&s_info_array[i]); if(ret==-1){ sys_err("pthread_create error"); } pthread_detach(tid); i++; } return 0; }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。