UNIX网络编程(UNP) 第二十六章学习笔记

概述

传统unix中,如果一个进程需要另一个实体完成某事,就会fork一个子进程然后让其执行。

但是fork调用存在着以下几个问题:

  • fork是昂贵的。fork要将父进程的内存映像复制到子进程,并在子进程复制所有描述符。即便现在使用了“写时复制”技术,fork依然是昂贵的

  • fork返回之后,父子进程之间的通信需要进程间通信(IPC)。调用fork之前父进程向子进程传递信息是容易的,但是子进程向父进程返回信息确很困难

线程有助于解决这些问题,因为

  • 线程创建成本较为低廉,比进程的创建可能快10-100倍
  • 线程共享相同的全局内存,使得线程之间易于共享信息
基本线程函数:创建和终止
pthread_create函数

当程序由exec启动执行时候,称为初始线程或者主线程的单个线程就创建了。其他都用pthread_create函数创建。

int pthread_create(pthread_t *tid,
		const pthread_attr_t * attr,
		void *(*func)(void * ),
		void * arg);

当线程创建成功的时候tid返回线程ID(pthraed_t);我们可以通过attr设置诸如线程优先级、初始栈大小、是否是守护线程等,一般可以用NULL表示使用默认值;func表示要该线程执行的函数,线程会调用该函数,然后通过显式的调用pthread_exit或者隐式的终止(通过return),函数的调用参数由arg指定,如果要传入多个数值,需要封装成struct然后再传入。

线程如果创建成功,函数返回0,否则返回正值表示具体错误(而不是返回-1并且设置errno)

pthread_join函数

我们可以用join来等待一个给定线程的终止,类似于waitpid。为此我们需要制定等待线程的tid(而不能等待任意一个线程结束)

int pthread_join(pthread_t *tid, void ** status)

如果status非空,那么所等待线程的返回值就会存入status指定的位置

pthread_self函数

线程可以调用该函数获知自己的tid(类似于进程中的getpid)

pthread_t pthread_self(void);
pthread_detach函数

一个线程要么是可汇合的(joinable,默认值),要么是脱离的(detached),一个可汇合的线程结束时候,线程ID和退出状态会留存到另一个线程对它调用pthread_join。脱离的线程终止的时候,所有相关的资源都被释放,不能等待终止。

int pthread_detach(pthread_t);

本函数一般是由想让自己脱离的线程调用,如以下语句

pthread_detach(pthread_self());
pthread_exit函数

让一个线程终止的方法之一就是调用pthread_exit函数

void pthread_exit(void * status);

如果该线程未曾脱离,其线程ID和退出状态会一直留存到调用进程内的某个其他线程对它调用pthread_join。

status不能指向局部于调用线程的对象,因为线程终止时候对象也消失。

线程终止的其他方法有:

  1. 启动线程的函数返回。其返回值就是线程的终止状态
  2. main返回或者任何线程调用了exit,进程终止,所有线程也随之终止。
使用线程的str_cli函数
#include "../unp.h"
#include <pthread.h>
void *copyto(void *);
static int sockfd;
static FILE *fp;

void str_cli(FILE *fp_arg, int sockfd_arg)
{
   
    char recvline[MAXLINE];
    pthread_t tid;
    sockfd = sockfd_arg;
    fp = fp_arg;
    pthread_create(&tid, NULL, copyto, NULL);
}
void *copyto(void *arg)
{
   
    char sendline[MAXLINE];
    while (Fgets(sendline, MAXLINE, fp) != NULL)
        Writen(sockfd, sendline, strlen(sendline));
    Shutdown(sockfd, SHUT_WR);
    return (NULL);
}
使用线程的TCP回射服务器程序
#include "../unp.h"
#include <pthread.h>
static void *doit(void *);
int main(int argc, char **argv)
{
   
    int listenfd, connfd;
    pthread_t tid;
    socklen_t addrlen, len;
    struct sockaddr *cliaddr;

    if (argc == 2)
        listenfd = Tcp_listen(NULL, argv[1], &addrlen);
    else if (argc == 3)
        listenfd = Tcp_listen(argv[1], argv[2], &addrlen);
    else
        err_quit("usage: tcpserv01 [ <host> ] <service or port>");
    cliaddr = Malloc(addrlen);

    for (;;)
    {
   
        len = addrlen;
        connfd = Accept(listenfd, cliaddr, &len);
        pthread_create(&tid, NULL, &doit, (void *)connfd);//这里直接就是一个连接一个线程
    }
}
static void *doit(void *arg)
{
   
    pthread_detach(pthread_self());
    str_echo((int)arg);
    Close((int)arg);
    return NULL;
}

注意在上述调用pthread_create中我们传入了整数类型connfd,ANSI C并不能保证这样做有用,只有在指针大小和整数类型大小一致的系统上才能起作用,不过绝大多数UNIX都符合这个特征。

那么问题来了?为什么我们不直接传入connfd地址呢?答案是不同的线程因此会共享该地址,从而互相干扰。

除此之外我们可以提供一个更好的方法

#include "../unp.h"
#include <pthread.h>
static void *doit(void *);
int main(int argc, char **argv)
{
   
    int listenfd, *iptr;
    pthread_t tid;
    socklen_t addrlen, len;
    struct sockaddr *cliaddr;

    if (argc == 2)
        listenfd = Tcp_listen(NULL, argv[1], &addrlen);
    
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值