第三十七章 POSIX线程(一)

POSIX线程库相关介绍

  与线程有关的函数构成了一个完整的系列,绝大多数函数的名字都有“pthread_”开头
  要使用这些函数库,都需要加入头文件“”, 链接的时候需要链接“-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 
#include 
#include 
#include 
#include 


#include 
#include 
#include 
#include 
#include 

#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 
#include 
#include 
#include 
#include 
#include 

#include 
#include 
#include 
#include 
#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线程(一))