zoukankan      html  css  js  c++  java
  • 一只简单的网络爬虫(基于linux C/C++)————线程相关

    爬虫里面采用了多线程的方式处理多个任务,以便支持并发的处理,把主函数那边算一个线程的话,加上一个DNS解析的线程,以及我们可以设置的max_job_num值,最多使用了1+1+max_job_num个线程。相关的线程封装如下:
    创建线程

    int create_thread(void *(*start_func)(void *), void * arg, pthread_t *pid, pthread_attr_t * pattr)
    {
        pthread_attr_t attr;
        pthread_t pt;
    
        if (pattr == NULL)
         {
            pattr = &attr;
            pthread_attr_init(pattr);//pthread_attr_init函数的作用初始化一个线程对象的属性,需要用pthread_attr_destroy函数对其去除初始化
            pthread_attr_setstacksize(pattr, 1024*1024);//设置线程堆栈的大小
            pthread_attr_setdetachstate(pattr, PTHREAD_CREATE_DETACHED);//设置线程分离
        }
    
        if (pid == NULL)
            pid = &pt;
    
        int rv = pthread_create(pid, pattr, start_func, arg);//创建线程
        pthread_attr_destroy(pattr);//取出线程对象属性的初始化
        return rv;
    }
    

    主要是使用了pthread_attr_init函数对线程的属性做了一些设置
    这里写图片描述
    Posix线程中的线程属性是一个pthread_attr_t类型,主要包括scope属性、detach属性、堆栈地址、堆栈大小、优先级。在pthread_create中,把第二个参数设置为NULL的话,将采用默认的属性配置。
    pthread_attr_t的主要属性的意义如下:
    __detachstate,表示新线程是否与进程中其他线程脱离同步, 如果设置为PTHREAD_CREATE_DETACHED 则新线程不能用pthread_join()来同步,且在退出时自行释放所占用的资源。缺省为PTHREAD_CREATE_JOINABLE状态。这个属性也可以在线程创建并运行以后用pthread_detach()来设置,而一旦设置为PTHREAD_CREATE_DETACH状态(不论是创建时设置还是运行时设置)则不能再恢复到PTHREAD_CREATE_JOINABLE状态。
    __schedpolicy,表示新线程的调度策略,主要包括SCHED_OTHER(正常、非实时)、SCHED_RR(实时、轮转法)和SCHED_FIFO(实时、先入先出)三种,缺省为SCHED_OTHER,后两种调度策略仅对超级用户有效。运行时可以用过pthread_setschedparam()来改变。
    __schedparam,一个struct sched_param结构,目前仅有一个sched_priority整型变量表示线程的运行优先级。这个参数仅当调度策略为实时(即SCHED_RR或SCHED_FIFO)时才有效,并可以在运行时通过pthread_setschedparam()函数来改变,缺省为0。
    __inheritsched,有两种值可供选择:PTHREAD_EXPLICIT_SCHED和PTHREAD_INHERIT_SCHED,前者表示新线程使用显式指定调度策略和调度参数(即attr中的值),而后者表示继承调用者线程的值。缺省为PTHREAD_EXPLICIT_SCHED。
    __scope,表示线程间竞争CPU的范围,也就是说线程优先级的有效范围。POSIX的标准中定义了两个值:PTHREAD_SCOPE_SYSTEM和PTHREAD_SCOPE_PROCESS,前者表示与系统中所有线程一起竞争CPU时间,后者表示仅与同进程中的线程竞争CPU。目前LinuxThreads仅实现了PTHREAD_SCOPE_SYSTEM一值。
    为了设置这些属性,POSIX定义了一系列属性设置函数,包括pthread_attr_init()、pthread_attr_destroy()和与各个属性相关的pthread_attr_getXXX/pthread_attr_setXXX函数。如下图:
    这里写图片描述
    主要的函数如下:
    1、pthread_attr_init
    功能: 对线程属性变量的初始化。
    头文件: pthread.h
    函数原型: int pthread_attr_init (pthread_attr_t* attr);
    函数传入值:attr:线程属性。
    函数返回值:成功: 0
    失败: -1
    2、pthread_attr_setscope
    功能: 设置线程 __scope 属性。scope属性表示线程间竞争CPU的范围,也就是说线程优先级的有效范围。POSIX的标准中定义了两个值:PTHREAD_SCOPE_SYSTEM和PTHREAD_SCOPE_PROCESS,前者表示与系统中所有线程一起竞争CPU时间,后者表示仅与同进程中的线程竞争CPU。默认为PTHREAD_SCOPE_PROCESS。目前LinuxThreads仅实现了PTHREAD_SCOPE_SYSTEM一值。
    头文件: pthread.h
    函数原型: int pthread_attr_setscope (pthread_attr_t* attr, int scope);
    函数传入值:attr: 线程属性。
    scope:PTHREAD_SCOPE_SYSTEM,表示与系统中所有线程一起竞争CPU时间,
    PTHREAD_SCOPE_PROCESS,表示仅与同进程中的线程竞争CPU
    函数返回值:成功: 0
    失败: -1
    3、pthread_attr_setdetachstate
    功能: 设置线程detachstate属性。该表示新线程是否与进程中其他线程脱离同步,如果设置为PTHREAD_CREATE_DETACHED则新线程不能用pthread_join()来同步,且在退出时自行释放所占用的资源。缺省为PTHREAD_CREATE_JOINABLE状态。这个属性也可以在线程创建并运行以后用pthread_detach()来设置,而一旦设置为PTHREAD_CREATE_DETACH状态(不论是创建时设置还是运行时设置)则不能再恢复到PTHREAD_CREATE_JOINABLE状态。
    头文件: phread.h
    函数原型: int pthread_attr_setdetachstate (pthread_attr_t* attr, int detachstate);
    函数传入值:attr:线程属性。
    detachstate:PTHREAD_CREATE_DETACHED,不能用pthread_join()来同步,且在退出时自行释放所占用的资源
    PTHREAD_CREATE_JOINABLE,能用pthread_join()来同步
    函数返回值:成功: 0
    失败: -1
    4、pthread_attr_setschedparam
    功能: 设置线程schedparam属性,即调用的优先级。
    头文件: pthread.h
    函数原型: int pthread_attr_setschedparam (pthread_attr_t* attr, struct sched_param* param);
    函数传入值:attr:线程属性。
    param:线程优先级。一个struct sched_param结构,目前仅有一个sched_priority整型变量表示线程的运行优先级。这个参数仅当调度策略为实时(即SCHED_RR或SCHED_FIFO)时才有效,并可以在运行时通过pthread_setschedparam()函数来改变,缺省为0
    函数返回值:成功: 0
    失败: -1
    5、pthread_attr_getschedparam
    功能: 得到线程优先级。
    头文件: pthread.h
    函数原型: int pthread_attr_getschedparam (pthread_attr_t* attr, struct sched_param* param);
    函数传入值:attr:线程属性;
    param:线程优先级;
    函数返回值:成功: 0
    失败: -1
    具体的函数可以通过man查得,下面谈谈pthread_attr_setdetachstate
    在任何一个时间点上,线程是可结合的(joinable),或者是分离的(detached)。一个可结合的线程能够被其他线程收回其资源和杀死;在被其他线程回收之前,它的存储器资源(如栈)是不释放的。相反,一个分离的线程是不能被其他线程回收或杀死的,它的存储器资源在它终止时由系统自动释放。
    线程的分离状态决定一个线程以什么样的方式来终止自己。在默认情况下线程是非分离状态的,这种情况下,原有的线程等待创建的线程结束。只有当pthread_join()函数返回时,创建的线程才算终止,才能释放自己占用的系统资源。而分离线程不是这样子的,它没有被其他的线程所等待,自己运行结束了,线程也就终止了,马上释放系统资源。程序员应该根据自己的需要,选择适当的分离状态。所以如果我们在创建线程时就知道不需要了解线程的终止状态,则可以pthread_attr_t结构中的detachstate线程属性,让线程以分离状态启动。
    爬虫里使用的是线程分离。
    线程(任务)开始

    void begin_thread()
    {
        SPIDER_LOG(SPIDER_LEVEL_DEBUG, "Begin Thread %lu", pthread_self());//获得线程自身的ID
    }

    该函数只是简单的打印一下自身id日志
    线程(任务)结束

    下面这个函数结束了一个任务,其实线程是自动结束的,因为之前设置了线程分离的属性,在线程函数执行结束后就会终止并释放资源,并没有使用pthread_join()函数,因此该函数其实只是打印下标志说明线程结束,并且如果在事件数允许的情况下调用attach_epoll_task(),该函数执行了发送请求,注册epoll事件等操作,然后又根据epoll_wait函数返回的事件数又创建若干线程来执行任务,如果事件数达到设置的上限,则会在其他线程结束的时候再创建新的线程来处理任务

    //结束一个任务,只是结束任务, 因为设置了线程分离,所以回调函数执行结束后便会结束该线程
    //但是如果任务数允许,则会创建新的任务
    void end_thread()
    {
        pthread_mutex_lock(&gctn_lock); 
        int left = g_conf->max_job_num - (--g_cur_thread_num);//刷新剩下的任务数
        if (left == 1) 
        {//创建一些新的任务,主函数那里只是处理原来的那些url
            // can start one thread  
            attach_epoll_task();//该函数执行了发送请求,注册epoll事件等操作
        } 
        else if (left > 1) 
        {
            // can start two thread  
            attach_epoll_task();
            attach_epoll_task();
        } 
        else 
        {//要先等待其他的事件退出,才能开启新的任务
            // have reached g_conf->max_job_num , do nothing  
        }
        //打印当前线程的id以及剩下的任务数
        SPIDER_LOG(SPIDER_LEVEL_DEBUG, "End Thread %lu, cur_thread_num=%d", pthread_self(), g_cur_thread_num);
        pthread_mutex_unlock(&gctn_lock);   
    }
    

    因此该爬虫没有使用线程池,而是每次创建新的线程处理事件,然后事件函数执行完毕自动结束线程,循环这个过程。
    线程属性的相关知识可以参考这里

  • 相关阅读:
    理解C语言中指针常量和常量指针区别!不要再搞混了~
    哪座城市可以安放程序员的灵魂,一线城市与二三线城市该如何择别?
    Linux 之父如何定义 "Linux" !主要想让黑客、计算机学生使用,学习和享受!
    程序员的凡尔赛文学!作为低调人群的程序员,“凡”起来又是怎样的一番景象呢?
    40个Java集合面试问题和答案
    从关系型数据库到非关系型数据库
    redis安装报错
    redis简介
    不满足依赖关系
    EL表达式中引用隐式变量
  • 原文地址:https://www.cnblogs.com/sigma0-/p/12630465.html
Copyright © 2011-2022 走看看