第三十七章 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时,把相应的生产数据库明文密码也一并上传了,导致了相 ...
 
随机推荐
- 【柠檬班】jmeter 不写代码,秒秒钟提取动态列表最后一个值
			
在用jmeter做接口测试时,我们经常会遇到,一个接口返回一个json串,在这个json串中,某个节点的值是一个列表,而且这个列表的长度是动态变化的.如: 获取用户列表,用户信息是个列表,类似的接 ...
 - MongoDB 学习笔记之 删除数据,集合,数据库
			
删除数据,集合,数据库: 删除一个文档: db.media.deleteOne({"name": "Sky"}) 删除多个文档: db.media.delete ...
 - 阿里云服务器ecs + tomcat + 域名解析 部署web页面
			
1.购买ecs:https://www.aliyun.com/product/ecs?spm=5176.12825654.eofdhaal5.2.3bf92c4aYOB7gL&aly_as=A ...
 - python编程基础之二
			
交互式: 此处以windows为例:开始->运行->cmd,输入python 交互式界面 优点:即时,所见即所得 缺点:代码不可复用,根本无法进行维护 退出:exit() 代码是顺序执行: ...
 - SSH服务协议
			
1.SSH介绍: SSH 是Secure Shell Protocol 的简写,由IETF网络小组(Network Working Group)制定:在进行数据传输之前,SSH先对联机数据包通过加密技 ...
 - 第10项:重写equals时请遵守通用约定
			
重写equals方法看起来似乎很简单,但是有许多重写方式会导致错误,而且后果非常严重.最容易避免这类问题的办法就是不覆盖equals方法,在这种情况下,类的每个实例都只能与它自身相等.如果满足了以 ...
 - C#用抽象类定义几何图形
			
using System;/*using System.Data;*/namespace tx{ abstract class tx { public double chan ...
 - Java基础之集合框架(Collection接口和List接口)
			
首先我们说说集合有什么作用. 一.集合的作用 1.在类的内部,对数据进行组织: 2.简单而快速的搜索大数量的条目: 3.有的集合接口,提供一系列排列有序的元素,并且可以在序列中间快速的插入或者删除有关 ...
 - django根据已有数据库表生成model类
			
django根据已有数据库表生成model类 创建一个Django项目 django-admin startproject 'xxxx' 修改setting文件,在setting里面设置你要连接的数据 ...
 - 手写OOXML文档——导出xlsx格式表格文档
			
一.准备工作: 2个js库,另外把样式文件抽离出来 require('file-saver'); import JSZip from 'jszip' import {stylesData,theme1 ...