第三十七章 POSIX线程(一)
POSIX线程库相关介绍
与线程有关的函数构成了一个完整的系列,绝大多数函数的名字都有“pthread_”开头
要使用这些函数库,都需要加入头文件“<pthread.h>”, 链接的时候需要链接“-lpthread”
pthread_create
功能:
创建一个线程
原型:
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
void *(*start_routine) (void *), void *arg);
参数:
thread : 返回线程ID
attr : 设置线程属性,为NULL时表示使用默认属性
start_routine : 是个函数地址,线程启动后要执行的函数
arg : 传给线程启动函数的参数
返回值:
成功 :0
失败 : 返回错误码
pthread_exit
功能:
线程终止
原型:
void pthread_exit(void *retval);
参数:
retval :retval不要指向一个局部变量
返回值:
无返回值,跟进程一样,线程结束的时候无法返回到它的调用者(自身)
pthread_join
功能:
等待线程结束
原型:
int pthread_join(pthread_t thread, void **retval);
参数:
thread :线程ID
retval : 它指向一个指针,指向线程的返回值
返回值:
成功 : 0
失败 : 返回错误码
pthread_detach
功能:
分离线程
原型:
int pthread_detach(pthread_t thread);
参数:
thread :线程ID
返回值:
成功 : 0
失败 : 返回错误码
pthread_self
功能:
返回线程的ID
原型:
int pthread_self(void);
返回值:
总是成功,返回调用此函数的线程ID
pthread_cancle
功能:
取消一个执行中的线程
原型:
int pthread_cancel(pthread_t thread);
参数:
thread : 线程ID
返回值:
成功 : 0
失败 : 返回错误码
错误检查
- 传统的一些函数是,成功返回0, 失败返回-1,并且对全局变量errno赋值以指示错误
- pthread函数出错时不会设置全局变量errno(而大部分其它POSIX函数会这样做)。而是将错误代码通过返回值返回
- pthread同样也提供了线程内的errno变量,以支持其它使用errno的代码。对于pthread函数的错误设置,建议通过返回值判定,因为读取返回值要比读取线程内的errno变量的开销更小
进程线程对比
属性 | 进程 | 线程 |
---|---|---|
ID | pid_t | pthread_t |
创建 | fork | pthread_create |
等待 | waitpid | pthread_join |
僵尸 | waitpid | pthread_join、pthread_detach |
退出(自杀) | exit,return | pthread_exit,return |
他杀 | kill | pthread_cancel |
用线程实现回射客户端、服务器端
pserver.c
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <pthread.h>
#define ERR_EXIT(m)\
do\
{\
perror(m);\
exit(EXIT_FAILURE);\
}while(0)
void do_service(int conn)
{
char recvbuf[1024] = {0};
while(1)
{
memset(recvbuf,0,sizeof(recvbuf));
int ret = read(conn,recvbuf,sizeof(recvbuf));
if(ret == 0)
{
printf("client close\n");
break;
}
else if(ret == -1)
ERR_EXIT("read");
fputs(recvbuf,stdout);
write(conn,recvbuf,ret);
}
}
void* thread_routine(void *arg)
{
pthread_detach(pthread_self());
// 使用取地址的方式获取
//int conn = *((int*)arg);
// 使用强制转换的方式获取
// int conn = (int)arg;
// 使用取地址的方式获取,释放空间
int conn = *((int*)arg);
free(arg);
do_service(conn);
printf("exit thread %lu ...\n",pthread_self());
return NULL;
}
int main(void)
{
int listenfd;
if((listenfd = socket(PF_INET,SOCK_STREAM,IPPROTO_TCP)) < 0)
ERR_EXIT("socket");
struct sockaddr_in serv_addr;
memset(&serv_addr,0,sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(5188);
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
// serv_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
// inet_aton("127.0.0.1",&serv_addr.sin_addr);
int on = 1;
if(setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR,&on,sizeof(on)) < 0)
ERR_EXIT("setsockopt");
if(bind(listenfd, (struct sockaddr*)(&serv_addr), sizeof(serv_addr)) < 0)
ERR_EXIT("bind");
if(listen(listenfd, SOMAXCONN) < 0)
ERR_EXIT("listen");
struct sockaddr_in cli_addr;
socklen_t cli_len = sizeof(cli_addr);
int conn;
while(1)
{
if((conn = accept(listenfd, (struct sockaddr *)(&cli_addr), &cli_len)) < 0)
ERR_EXIT("accept");
printf("ip : %s port : %d\n",inet_ntoa(cli_addr.sin_addr),ntohs(cli_addr.sin_port));
pthread_t tid;
int ret;
// 使用这种方式存在一定的问题,因为是&conn,如果accept返回后,thread_routine还没有来得及处理上一个conn,conn将被改变,导致上一次连接无法处理
// 最好不要使用指针传递,应使用值传递;
//if( (ret = pthread_create(&tid, NULL, thread_routine, (void*)&conn)) != 0 )
//使用这种方式,将int类型装换成无类型指针,int是4个字节,指针也是4个字节;但是这种做法是不可移植的,不同的操作系统,指针所占的字节数不一样
// if( (ret = pthread_create(&tid, NULL, thread_routine, (void*)conn)) != 0 )
//申请一块单独的内存放conn,取出后释放掉
int *p = malloc(sizeof(int));
*p = conn;
if( (ret = pthread_create(&tid, NULL, thread_routine, p)) != 0 )
{
fprintf(stderr, "pthread_create : %s\n", strerror(ret));
exit(EXIT_FAILURE);
}
}
return 0;
}
pclient.c
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#define ERR_EXIT(m)\
do\
{\
perror(m);\
exit(EXIT_FAILURE);\
}while(0)
void handler(int sig)
{
printf("recv a sig :%d\n",sig);
exit(EXIT_SUCCESS);
}
int main(void)
{
int sock;
// socket(PF_INET,SOCK_STREAM,0);
if((sock = socket(PF_INET,SOCK_STREAM,IPPROTO_TCP)) < 0)
ERR_EXIT("socket");
struct sockaddr_in serv_addr;
memset(&serv_addr,0,sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(5188);
serv_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
if(connect(sock,(struct sockaddr*)&serv_addr,sizeof(serv_addr)) < 0 )
ERR_EXIT("connect");
pid_t pid;
pid = fork();
if(pid < 0)
ERR_EXIT("fork");
if(pid == 0)
{
char recvbuf[1024] = {0};
while(1)
{
memset(recvbuf,0,sizeof(recvbuf));
int ret = read(sock,recvbuf,sizeof(recvbuf));
if (ret == -1)
ERR_EXIT("read");
else if(ret == 0)
{
printf("peer close\n");
break;
}
fputs(recvbuf,stdout);
}
close(sock);
kill(getppid(),SIGUSR1);
}
else
{
signal(SIGUSR1,handler);
char sendbuf[1024] = {0};
while(fgets(sendbuf,sizeof(sendbuf),stdin) != NULL)
{
write(sock,sendbuf,strlen(sendbuf));
memset(sendbuf,0,sizeof(sendbuf));
}
close(sock);
}
return 0;
}
第三十七章 POSIX线程(一)的更多相关文章
- “全栈2019”Java多线程第三十七章:如何让等待的线程无法被中断
难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java多 ...
- 程序员编程艺术第三十六~三十七章、搜索智能提示suggestion,附近点搜索
第三十六~三十七章.搜索智能提示suggestion,附近地点搜索 作者:July.致谢:caopengcs.胡果果.时间:二零一三年九月七日. 题记 写博的近三年,整理了太多太多的笔试面试题,如微软 ...
- Gradle 1.12用户指南翻译——第三十七章. OSGi 插件
本文由CSDN博客万一博主翻译,其他章节的翻译请参见: http://blog.csdn.net/column/details/gradle-translation.html 翻译项目请关注Githu ...
- “全栈2019”Java第三十七章:类与字段
难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java第 ...
- 第三十八章 POSIX线程(二)
线程属性 初始化与销毁属性 int pthread_attr_init(pthread_attr_t *attr); int pthread_attr_destroy(pthread_attr_t * ...
- 第三十七章 springboot+docker(手动部署)
一.下载centos镜像 docker pull hub.c.163.com/library/centos:latest docker tag containId centos:7 docker ru ...
- 三十七、Linux 线程——线程清理和控制函数、进程和线程启动方式比较、线程的状态转换
37.1 线程清理和控制函数 #include <pthread.h> void pthread_cleanup_push(void (* rtn)(void *), void *arg) ...
- 【第三十七章】 springboot+docker(手动部署)
一.下载centos镜像 docker pull hub.c.163.com/library/centos:latest docker tag containId centos:7 docker ru ...
- SpringBoot | 第三十七章:集成Jasypt实现配置项加密
前言 近期在进行项目安全方面评审时,质量管理部门有提出需要对配置文件中的敏高文件进行加密处理,避免了信息泄露问题.想想前段时间某公司上传github时,把相应的生产数据库明文密码也一并上传了,导致了相 ...
随机推荐
- mysql数据库设计规则总结
MySQL数据库设计总结 规则1:一般情况可以选择MyISAM存储引擎,如果需要事务支持必须使用InnoDB存储引擎. 注意:MyISAM存储引擎 B-tree索引有一个很大的限制:参与一个索引的 ...
- spring源码分析系列2:Bean与BeanDefinition关系
接口表示一种能力,实现了一个接口,即拥有一种能力. BeanDefinition与Bean的关系, 就好比类与对象的关系. 类在spring的数据结构就是BeanDefinition.根据BeanDe ...
- Rust入坑指南:核心概念
如果说前面的坑我们一直在用小铲子挖的话,那么今天的坑就是用挖掘机挖的. 今天要介绍的是Rust的一个核心概念:Ownership.全文将分为什么是Ownership以及Ownership的传递类型两部 ...
- link 和 @import 的区别是什么?
link语法结构: <link href="url" rel="stylesheet" type="text/css"> @im ...
- css3——box-sizing属性
很多朋友们可能会疑惑,不知道box-sizing属性是有什么作用,自己也很少会用到,但是想必不少人在做网页布局的时候经常遇到一个问题就是我明明设置了父元素设置了假如是宽高500px,5个子元素左浮动设 ...
- 基于Spring AOP实现的权限控制
1.AOP简介 AOP,面向切面编程,往往被定义为促使软件系统实现关注点的分离的技术.系统是由许多不同的组件所组成的,每一个组件负责一块特定的功能.除了实现自身核心功能之外,这些组件还经常承担着额外的 ...
- .NET Core使用App.Metrics监控消息队列(一):初探
一.简介 App Metrics是一个开放源代码和跨平台的.NET库,用于记录应用程序中的指标.App Metrics可以在.NET Core或也支持.NET 4.5.2的完整.NET框架上运行. A ...
- EasyExcel 轻松灵活读取Excel内容
写在前面 Java 后端程序员应该会遇到读取 Excel 信息到 DB 等相关需求,脑海中可能突然间想起 Apache POI 这个技术解决方案,但是当 Excel 的数据量非常大的时候,你也许发现, ...
- Oracle数据库提权(低权限提升至dba)
0x01 Oracle存储过程”缺陷” 在 Oracle 的存储过程中,有一个有趣的特点:运行权限.运行权限分为两种,definer 和 invoker. definer 为函数创建者的权限,而 in ...
- JedisCluster与keys/scan查找
最近买了几个专栏,关于算法.JVM.redis,学不过来.主要是身体也不太好,视物光斑转头疼的问题出现越来越频繁.再加上早上起来嗓子痒打喷嚏.很烦. 稍记录一下redis集群的问题: 1.scan在集 ...