linux网络编程-socket(37)
发布日期:2021-08-28 12:01:27 浏览次数:2 分类:技术文章

本文共 7605 字,大约阅读时间需要 25 分钟。

 

在编程的时候需要加上对应pthread开头的头文件,gcc编译的时候需要加了-lpthread选项

第三个参数是线程的入口参数,函数的参数是void*,返回值是void*,第四个参数传递给线程函数的参数

 

如果创建线程失败,返回值是一个错误码,错误码通过返回值返回,我们要进行错误检查就检查函数的返回值

函数简介

pthread_create是UNIX环境创建线程函数

头文件

#include<pthread.h>

函数声明

int pthread_create( *restrict tidp,const pthread_attr_t *restrict_attr,void*(*start_rtn)(void*),void *restrict arg);

返回值

若成功则返回0,否则返回出错编号

参数

第一个参数为指向线程的指针。

第二个参数用来设置线程属性。

第三个参数是线程运行函数的起始地址。

最后一个参数是运行函数的参数。

另外

在编译时注意加上-lpthread参数,以调用静态链接库。因为pthread并非Linux系统的默认库

pthread_join函数及线程

 

 

pthread_join使一个线程等待另一个线程结束。

 

如果你的主线程,也就是main函数执行的那个线程,在你其他线程退出之前就已经退出,那么带来的bug则不可估量。通过pthread_join函数会让主线程阻塞,直到所有线程都已经退出。

  int pthread_join(pthread_t thread, void **value_ptr);

    thread:等待退出线程的线程号。
    value_ptr:退出线程的返回值。

代码中如果没有pthread_join主线程会很快结束从而使整个进程结束,从而使创建的线程没有机会开始执行就结束了。加入pthread_join后,主线程会一直等待直到等待的线程结束自己才结束,使创建的线程有机会执行。

我们来看一个简单的例子:

 

#include 
#include
#include
#include
#include
#include
#include
/**定义一个宏,输出错误信息并且退出 */#define ERR_EXIT(m) \do \{\perror(m);\exit(EXIT_FAILURE);\}while(0)void*thread_exc(void*arg){ int j = 0; for(j = 0 ;j < 20 ;j++){ printf("sub Thread run%d\n",j); } return 0;}int main(){ //这里thread_id必须定义成 pthread_t类型 unsigned long int ,无符号的long类型,不能定义成int类型 pthread_t thread_id ; int ret; if((ret = pthread_create(&thread_id,NULL,thread_exc,NULL) != 0)){ ERR_EXIT("线程创建失败"); } int i = 0; for(i = 0 ;i < 20 ;i++){ printf("main Thread run%d\n",i); } printf(" thread_id =%ud\n",thread_id); if((ret = pthread_join(thread_id,NULL))!= 0){ ERR_EXIT("现在加入失败"); } return 0;}

上面代码有一个相当重要的地方,在c语言中的类型定义

pthread_create函数的第一个参数是输入一个pthread_t类型
typedef unsigned long int pthread_t;
//come from /usr/include/bits/pthreadtypes.h
用途:pthread_t用于声明线程ID。
sizeof(pthread_t) =8
pthread_t,在使用printf打印时,应转换为u类型。
 
我在写代码的时候不小心写成了int 类型,结果一直提示egmentation fault (core dumped)
这里一定要小心呀

 

 可以调用pthread_exit来退出线程

#include 
#include
#include
#include
#include
#include
#include
/**定义一个宏,输出错误信息并且退出 */#define ERR_EXIT(m) \do \{\perror(m);\exit(EXIT_FAILURE);\}while(0)void*thread_exc(void*arg){ int j = 0; for(j = 0 ;j < 20 ;j++){ printf("sub Thread run%d\n",j); if(j == 3){ //线程退出的时候会将返回值传递给pthread_join的第二个参数中 pthread_exit("abc"); } } return 0;}int main(){ //这里thread_id必须定义成 pthread_t类型 unsigned long int ,无符号的long类型,不能定义成int类型 pthread_t thread_id ; int ret; if((ret = pthread_create(&thread_id,NULL,thread_exc,NULL) != 0)){ ERR_EXIT("线程创建失败"); } int i = 0; for(i = 0 ;i < 20 ;i++){ printf("main Thread run%d\n",i); } printf(" thread_id =%ud\n",thread_id); void*value; if((ret = pthread_join(thread_id,&value))!= 0){ ERR_EXIT("现在加入失败"); } printf("pthread_exit return msg is %s\n",((char*)value)); return 0;}

 

例如当线程中i=3的时候,退出线程 

pthread_exit退出的时候可以携带返回值,返回值存储在
pthread_join的第二个参数中,我们来看程序运行的代码 代码名称是thread.c我们使用gcc进行编译 gcc thread.c -g -o thread -lpthread 我们看程序运行的结果

value中的值就是线程退出的时候的返回值。

 如果不调用pthred_exit退出线程,当线程执行完成之后,也可以返回值,返回值也存储在value中我们来看下面的代码

 

#include 
#include
#include
#include
#include
#include
#include
/**定义一个宏,输出错误信息并且退出 */#define ERR_EXIT(m) \do \{\perror(m);\exit(EXIT_FAILURE);\}while(0)void*thread_exc(void*arg){ int j = 0; for(j = 0 ;j < 20 ;j++){ printf("sub Thread run%d\n",j); } return "edf";}int main(){ //这里thread_id必须定义成 pthread_t类型 unsigned long int ,无符号的long类型,不能定义成int类型 pthread_t thread_id ; int ret; if((ret = pthread_create(&thread_id,NULL,thread_exc,NULL) != 0)){ ERR_EXIT("线程创建失败"); } int i = 0; for(i = 0 ;i < 20 ;i++){ printf("main Thread run%d\n",i); } printf(" thread_id =%ud\n",thread_id); void*value; if((ret = pthread_join(thread_id,&value))!= 0){ ERR_EXIT("现在加入失败"); } printf("pthread_exit return msg is %s\n",((char*)value)); return 0;}

 

程序运行的结果是:

 僵尸线程:

如果是新创建的线程运行结束了,而主线程一直阻塞没有调用pthread_join函数,导致子线程的资源无法释放,那么子线程就会一直处于僵尸状态

我们可以设置线程的属性来必须线程处于僵尸状态,因为有的时候主线程可能不会主动去调用pthread_join函数

可以使用pthread_detach函数来避免僵尸线程,将线程的属性设置成脱离的状态,脱离的线程不会产生僵尸线程

pthread_detach():设置线程为可分离状态,pthread有两种状态joinable状态和unjoinable状态

pthread_self():获得自身的线程号

pthread_join():使一个线程等待另一个线程结束

pthread_self():获得自身的线程

pthread_create():创建一个新的线程

pthread_detach():设置线程为可分离状态,pthread有两种状态joinable状态和unjoinable状态

pthread_exit():通过自身来结束线程,并释放资源

pthread_cancel():通过其他线程调用释放资源

pthread_cancel()可以杀死其他的线程

 

例如主线程要取消子线程,可以调用pthread_cancel

现在我们使用多线程的方式,把对应的代码,改成服务器能够支持多个客户端连接请求相应的功能

首先我们应该把服务器端accept等待客户端请求的代码放在while循环中,让服务器一直运行来监听客户端的请求

第二:监听到客户端的请求之后,每一个客户端对应一个线程来处理客户端的请求

我们来修改下服务器端的代码:

 

#include 
#include
#include
#include
#include
#include
#include
/**定义一个宏,输出错误信息并且退出 */#define ERR_EXIT(m) \do \{\perror(m);\exit(EXIT_FAILURE);\}while(0)void*thread_exc(void* arg){ pthread_detach(pthread_self()); //将线程设置成分离状态,避免僵尸线程 int clnt_sock = *((int*)arg); //记得关闭指针 free(arg); char revbuf[1024]; while(1){ memset(revbuf,0,sizeof(revbuf)); int len = read(clnt_sock,revbuf,sizeof(revbuf)); //len读到数据的字节长度 if(len == 0){ //说明客户端终止了数据的发送 break; } fputs(revbuf,stdout); //读到多少数据就给客户端返回多少字节的数据 write(clnt_sock,revbuf,len); } close(clnt_sock); //记得关闭线程 }int main(int argc, char *argv[]){ int serv_sock; int clnt_sock; struct sockaddr_in serv_addr; struct sockaddr_in clnt_addr; socklen_t clnt_addr_size; serv_sock = socket(AF_INET, SOCK_STREAM, 0); if (serv_sock == -1) { ERR_EXIT("socket创建失败"); } memset(&serv_addr, 0, sizeof(serv_addr)); serv_addr.sin_family = AF_INET; serv_addr.sin_addr.s_addr = htonl(INADDR_ANY); serv_addr.sin_port = htons(9999); if (bind(serv_sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) == -1){ ERR_EXIT("bind失败"); } //SOMAXCON系统默认的最大的客户端的连接数据 , (listen(serv_sock, 5)表示最大允许5个客户端的连接 if (listen(serv_sock, SOMAXCONN) == -1){ ERR_EXIT("listen失败"); } while(1){ //在while循环中一直等待客户端的监听 clnt_addr_size = sizeof(clnt_addr); clnt_sock = accept(serv_sock, (struct sockaddr*)&clnt_addr, &clnt_addr_size); if (clnt_sock == -1){ ERR_EXIT("accept失败"); } //每一个客户端的请求都开启一个线程进行处理 pthread_t thread_id ; int ret; //将clnt_sock通过第三个参数传递到线程函数中 int * p = (int*)malloc(sizeof(int)); *p = clnt_sock; if((ret = pthread_create(&thread_id,NULL,thread_exc, p ))!= 0){ ERR_EXIT("线程创建失败"); } } close(serv_sock);return 0;}void error_handling(char *message){fputs(message, stderr);fputc('\n', stderr);exit(1);}

 

客户端的代码如下:

#include 
#include
#include
#include
#include
#include
/**定义一个宏,输出错误信息并且退出 */#define ERR_EXIT(m) \ do \ {\ perror(m);\ exit(EXIT_FAILURE);\ }while(0)int main(int argc, char *argv[]){ int serv_sock; struct sockaddr_in serv_addr; serv_sock = socket(AF_INET, SOCK_STREAM, 0); if (serv_sock == -1) { ERR_EXIT("socket创建失败"); } memset(&serv_addr, 0, sizeof(serv_addr)); serv_addr.sin_family = AF_INET; serv_addr.sin_addr.s_addr = inet_addr("127.0.0.1"); serv_addr.sin_port = htons(9999); if((connect(serv_sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr)))<0){ ERR_EXIT("客户端connect失败"); } char revbuf[1024]; char sendbuf[1024]; memset(revbuf,0,sizeof(revbuf)); memset(sendbuf,0,sizeof(revbuf)); while((fgets(sendbuf,sizeof(sendbuf),stdin)!= NULL)){ write(serv_sock,sendbuf,strlen(sendbuf)); read(serv_sock,revbuf,sizeof(revbuf)); fputs(revbuf,stdout); //读到多少数据就给客户端返回多少字节的数据 memset(sendbuf,0,sizeof(revbuf)); memset(revbuf,0,sizeof(revbuf)); } close(serv_sock); return 0;}

 

 

 

 

 

 

转载于:https://www.cnblogs.com/kebibuluan/p/7091374.html

转载地址:https://blog.csdn.net/weixin_33957648/article/details/93542273 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:WinDbg:栈帧的含义
下一篇:SpringMVC 学习笔记(7)spring和springmvc的整合

发表评论

最新留言

路过,博主的博客真漂亮。。
[***.116.15.85]2024年04月23日 09时03分50秒

关于作者

    喝酒易醉,品茶养心,人生如梦,品茶悟道,何以解忧?唯有杜康!
-- 愿君每日到此一游!

推荐文章

C++核心准则ES.61:使用delete[]销毁数组,使用delete销毁对象 2019-04-28
C++核心准则ES.62:不要比较不同数组中的元素地址 2019-04-28
C++核心准则ES.63:不要分割处理对象 2019-04-28
在Ubuntu20.04环境中构建C++20开发环境(GCC11) 2019-04-28
C++20新特性简介-Concepts 2019-04-28
C++核心准则ES.71: 如果可以,使用范围for代替普通的for语句 2019-04-28
C++核心准则ES.72:如果存在明显的循环变量,for语句要好于while语句 2019-04-28
C++核心准则ES.73:如果没有明显的循环变量,while语句要好于for语句 2019-04-28
C++核心准则ES.76:避免使用goto语句 2019-04-28
C++核心准则ES.77:循环中尽量少用break和continue 2019-04-28
C++核心准则ES.78:不要依靠switch语句的隐式下沉处理 2019-04-28
C++核心准则E.13: 直接拥有一个对象所有权时永远不要抛出异常 2019-04-28
C++核心准则E.14:使用根据目的设计的用户定制类型异常(非内置类型) 2019-04-28
C++核心准则E.18:最小限度显式使用try/catch 2019-04-28
C++核心准则E.25:如果不能抛出异常,模仿RAII方式进行资源管理 2019-04-28
C++核心准则E.27:如果无法抛出异常,系统化运用错误处理代码 2019-04-28
C++核心准则E.28:避免基于全局状态的错误处理(例如errno) 2019-04-28
C++核心准则E.30:不要使用抛异常声明 2019-04-28
C++核心准则E.31:正确排列catch子句 2019-04-28
C++核心准则Con.1:默认情况下使对象不可修改 2019-04-28