多线程的socket编程

一、线程

上次我们聊到了多进程的编程,对于一台主机运行一个进程是需要耗费很多系统资源,这时候我们就可以选择多线程来解决问题。,线程是进程的一条执行路径。线程在Unix系统下,通常被称为轻量级的进程,线程虽然不是进 程,但却可以看作是Unix进程的表亲,所有的线程都是在同一进程空间运行,这也意味着多条线程将共享该进程中的全部系统资 源,如虚拟地址空间,文件描述符和信号处理等等。但同一进程中的多个线程有各自的调用栈(call stack),自己的寄存器环境 (register context),自己的线程本地存储(thread-local storage)。 一个进程可以有很多线程,每条线程并行执行不同的任务。

二、流程图

在这里插入图片描述

三、详细说明
1、创建线程

一个进程创建后,会首先生成一个缺省的线程,通常称这个线程为主线程(或称控制线程),子线程是由主线程调用pthread_creat()创建的。每个线程都有自己的线程ID,可以通过pthread_self()函数获取。最常见的线程模型中,除主线程较为特殊 之外,其他线程一旦被创建,相互之间就是对等关系,不存在隐含的层次关系。每个进程可创建的最大线程数由具体实现决定。

主线程和子线程的默认关系是:无论子线程执行完毕与否,一旦主线程执行完毕退出,所有 子线程执行都会终止。这时整个进程结束或僵死,部分线程保持一种终止执行但还未销毁的状态,而进程必须在其所有线程销毁 后销毁,这时进程处于僵死状态。线程函数执行完毕退出,或以其他非常方式终止,线程进入终止态,但是为线程分配的系统资 源不一定释放,可能在系统重启之前,一直都不能释放,终止态的线程,仍旧作为一个线程实体存在于操作系统中,什么时候销 毁,取决于线程属性。在这种情况下,主线程和子线程通常定义以下两种关系:

可会合(joinable):这种关系下,主线程需要明确执行等待操作,在子线程结束后,主线程的等待操作执行完毕,子线 程和主线程会合,这时主线程继续执行等待操作之后的下一步操作。主线程必须会合可会合的子线程。在主线程的线程函 数内部调用子线程对象的wait函数实现,即使子线程能够在主线程之前执行完毕,进入终止态,也必须执行会合操作,否 则,系统永远不会主动销毁线程,分配给该线程的系统资源也永远不会释放。

线程的分离状态决定一个线程以什么样的方式来终止自己,在默认的情况下,线程是非分离状态的,这种情况下,原有的线程 等待创建的线程结束,只有当pthread_join函数返回时,创建的线程才算终止,释放自己占用的系统资源,而分离线程没有被其 他的线程所等待,自己运行结束了,线程也就终止了,马上释放系统资源。

相分离(detached):表示子线程无需和主线程会合,也就是相分离的,这种情况下,子线程一旦进入终止状态,这种 方式常用在线程数较多的情况下,有时让主线程逐个等待子线程结束,或者让主线程安排每个子线程结束的等待顺序,是 很困难或不可能的,所以在并发子线程较多的情况下,这种方式也会经常使用。

2、pthread_creat()

pthread_creat()库函数,函数原型:int pthread_create(pthread_t *thread, const pthread_attr_t *attr,void *(*start_routine) (void *), void *arg);
函数的作用是:在调用过程中启动一个新线程。新线程通过调用
start_routine();arg作为start_routine()的唯一参数传递。

第一个参数thread是一个pthread_t类型的指针,他用来返回该线程的线程ID。每个线程都能够通过pthread_self()来获取 自己的线程ID(pthread_t类型)。

第二个参数是线程的属性,其类型是pthread_attr_t类型。

第三个参数start_routine是一个函数指针,它指向的函数原型是 void *func(void *),这是所创建的子线程要执行的任务 (函数)。

第四个参数arg就是传给了所调用的函数的参数,如果有多个参数需要传递给子线程则需要封装到一个结构体里传进去。

pthread_attr_t结构体如下:

typedef struct 
{
          int                        detachstate; //线程的分离状态 
  		int                        schedpolicy; //线程调度策略 
  		struct sched_param         schedparam;  //线程的调度参数 
  		int                        inheritsched;//线程的继承性 
   		int                        scope;       //线程的作用域
     	size_t                     guardsize;   //线程栈末尾的警戒缓冲区大小 
      	int                        stackaddr_set; 
       	void *                     stackaddr;   //线程栈的位置
       	size_t                     stacksize;    //线程栈的大小
}pthread_attr_t;

对于这些属性,我们需要设定的是线程的分离状态,如果有必要也需要修改每个线程的栈大小。每个线程创建后默认是joinable 状态,该状态需要主线程调用 pthread_join 等待它退出,否则子线程在结束时,内存资源不能得到释放造成内存泄漏。所以我 们创建线程时一般会将线程设置为分离状态,具体有两种方法:

  1. 线程里面调用 pthread_detach(pthread_self()) 这个方法最简单
  2. 在创建线程的属性设置里设置PTHREAD_CREATE_DETACHED属性

调用pthread_creat时我们首先需要调用int pthread_attr_init(pthread_attr_t *attr)来进行初始化;

int pthread_attr_init(pthread_attr_t *attr);

随之我们可以用int pthread_attr_setstacksize(pthread_attr_t *attr, size_t stacksize)为使用线程创建的线程分配的最小大小(字节);

 if( pthread_attr_setstacksize(&thread_attr, 120*1024) 

然后我们需要将线程设置成分离状态int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate);

int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate);

最后我们才来创建线程

if(pthread_create(thread_id, &thread_attr, thread_workname, thread_workarg))

其中thread_workname时一个void * *(func)(void *)的指针他指向函数。

三、示例代码
#include<stdio.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值