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线程(一)的更多相关文章

  1. “全栈2019”Java多线程第三十七章:如何让等待的线程无法被中断

    难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java多 ...

  2. 程序员编程艺术第三十六~三十七章、搜索智能提示suggestion,附近点搜索

    第三十六~三十七章.搜索智能提示suggestion,附近地点搜索 作者:July.致谢:caopengcs.胡果果.时间:二零一三年九月七日. 题记 写博的近三年,整理了太多太多的笔试面试题,如微软 ...

  3. Gradle 1.12用户指南翻译——第三十七章. OSGi 插件

    本文由CSDN博客万一博主翻译,其他章节的翻译请参见: http://blog.csdn.net/column/details/gradle-translation.html 翻译项目请关注Githu ...

  4. “全栈2019”Java第三十七章:类与字段

    难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java第 ...

  5. 第三十八章 POSIX线程(二)

    线程属性 初始化与销毁属性 int pthread_attr_init(pthread_attr_t *attr); int pthread_attr_destroy(pthread_attr_t * ...

  6. 第三十七章 springboot+docker(手动部署)

    一.下载centos镜像 docker pull hub.c.163.com/library/centos:latest docker tag containId centos:7 docker ru ...

  7. 三十七、Linux 线程——线程清理和控制函数、进程和线程启动方式比较、线程的状态转换

    37.1 线程清理和控制函数 #include <pthread.h> void pthread_cleanup_push(void (* rtn)(void *), void *arg) ...

  8. 【第三十七章】 springboot+docker(手动部署)

    一.下载centos镜像 docker pull hub.c.163.com/library/centos:latest docker tag containId centos:7 docker ru ...

  9. SpringBoot | 第三十七章:集成Jasypt实现配置项加密

    前言 近期在进行项目安全方面评审时,质量管理部门有提出需要对配置文件中的敏高文件进行加密处理,避免了信息泄露问题.想想前段时间某公司上传github时,把相应的生产数据库明文密码也一并上传了,导致了相 ...

随机推荐

  1. 我的mongoDb之旅(二)

    题序:上一回,咱们简单的操作了一些增减改查的操作,这一次,再来进行一场奇妙之旅 一.案例讲解 (1).字段有值与没值时的单条数据 第一条数据title这个字段(mysql用久了,习惯这么叫了)是没有数 ...

  2. Java 学习笔记之 线程interrupt方法

    线程interrupt方法: interrupt方法是用来停止线程的,但是他的使用效果并不像for+break那样,马上就停止循环. 调用interrupt()其实仅仅是在当前线程中打了一个停止标记, ...

  3. ELK 学习笔记之 Logstash安装

    Logstash安装: https://www.elastic.co/downloads/logstash 下载解压: tar –zxvf logstash-5.6.1.tar.gz 在/usr/lo ...

  4. 使用TryGetComponent取代GetComponent以避免Editor中的内存分配

    作为Unity开发人员,可能或多或少都会遇到过一个常见的Unity的GC分配问题——在Editor中使用GetComponent方法来获取一个不存在的Component时会分配额外的内存.就像下图 需 ...

  5. Vue学习系列(二)——组件详解

    前言 在上一篇初识Vue核心中,我们已经熟悉了vue的两大核心,理解了Vue的构建方式,通过基本的指令控制DOM,实现提高应用开发效率和可维护性.而这一篇呢,将对Vue视图组件的核心概念进行详细说明. ...

  6. Django跨域问题(CORS错误)

    Django跨域问题(CORS错误) 一.出现跨域问题(cors错误)的原因 通常情况下,A网页访问B服务器资源时,不满足以下三个条件其一就是跨域访问 协议不同 端口不同 主机不同 二.Django解 ...

  7. 2.RF中scalar,list和dict变量的定义和取值

    $:定义scalar变量:@定义list变量:&定义dict变量: $还用来取值,包含scalar, list和dict变量,如下example所示 1.定义scalar变量:set vari ...

  8. eclipse常用快捷键即项目操作

    快捷键: 1.代码提示:Alt+/ 2.撤销上一步操作:Ctrl+z:取消撤销:Ctrl+y: 3.如何注销一整段代码?☞▲第一种注释方法是每行代码前加//:先选中,然后按Ctrl+/:取消注销方法一 ...

  9. 线程封闭之栈封闭和ThreadLocal

    线程封闭 在多线程的环境中,我们经常使用锁来保证线程的安全,但是对于每个线程都要用的资源使用锁的话那么程序执行的效率就会受到影响,这个时候可以把这些资源变成线程封闭的形式. 1.栈封闭 所谓的栈封闭其 ...

  10. Ubuntu php安装xdebug

    1.安装xdebug扩展: sudo apt-get install php-xdebug 2.找到扩展的路径: 3.编辑php.ini文件,末尾加入,保存退出: [xdebug] zend_exte ...