PHP多进程(4) :内部多进程

说的都是只兼容unix 服务器的多进程,下面来讲讲在window 和 unix 都兼容的多进程(这里是泛指,下面的curl实际上是通过IO复用实现的)。

通过扩展实现多线程的典型例子是CURL,CURL 支持多线程的抓取网页的功能。

这部分过于抽象,所以,我先给出一个CURL并行抓取多个网页内容的一个分装类。这个类实际上很实用,

详细分析这些函数的内部实现将在下一个教程里面描述。

你可能不能很好的理解这个类,而且,php curl 官方主页上都有很多错误的例子,在讲述了其内部机制

后,你就能够明白了。

先看代码:

class Http_MultiRequest
{
    //要并行抓取的url 列表
    private $urls = array();

    //curl 的选项
    private $options;

    //构造函数
    function __construct($options = array())
    {
        $this->setOptions($options);
    }

    //设置url 列表
    function setUrls($urls)
    {
        $this->urls = $urls;
        return $this;
    }

    //设置选项
    function setOptions($options)
    {
        $options[CURLOPT_RETURNTRANSFER] = 1;
        if (isset($options[‘HTTP_POST‘]))
        {
            curl_setopt($ch, CURLOPT_POST, 1);
            curl_setopt($ch, CURLOPT_POSTFIELDS, $options[‘HTTP_POST‘]);
            unset($options[‘HTTP_POST‘]);
        }

        if (!isset($options[CURLOPT_USERAGENT]))
        {
            $options[CURLOPT_USERAGENT] = ‘Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1;)‘;
        }

        if (!isset($options[CURLOPT_FOLLOWLOCATION]))
        {
            $options[CURLOPT_FOLLOWLOCATION] = 1;
        }

        if (!isset($options[CURLOPT_HEADER]))
        {
            $options[CURLOPT_HEADER] = 0;
        }
        $this->options = $options;
    }

    //并行抓取所有的内容
    function exec()
    {
        if(empty($this->urls) || !is_array($this->urls))
        {
            return false;
        }
        $curl = $data = array();
        $mh = curl_multi_init();
        foreach($this->urls as $k => $v)
        {
            $curl[$k] = $this->addHandle($mh, $v);
        }
        $this->execMulitHandle($mh);
        foreach($this->urls as $k => $v)
        {
            $data[$k] = curl_multi_getcontent($curl[$k]);
            curl_multi_remove_handle($mh, $curl[$k]);
        }
        curl_multi_close($mh);
        return $data;
    }

    //只抓取一个网页的内容。
    function execOne($url)
    {
        if (empty($url)) {
            return false;
        }
        $ch = curl_init($url);
        $this->setOneOption($ch);
        $content = curl_exec($ch);
        curl_close($ch);
        return $content;
    }

    //内部函数,设置某个handle 的选项
    private function setOneOption($ch)
    {
        curl_setopt_array($ch, $this->options);
    }

    //添加一个新的并行抓取 handle
    private function addHandle($mh, $url)
    {
        $ch = curl_init($url);
        $this->setOneOption($ch);
        curl_multi_add_handle($mh, $ch);
        return $ch;
    }

    //并行执行(这样的写法是一个常见的错误,我这里还是采用这样的写法,这个写法
    //下载一个小文件都可能导致cup占用100%, 并且,这个循环会运行10万次以上
    //这是一个典型的不懂原理产生的错误。这个错误在PHP官方的文档上都相当的常见。)
    private function execMulitHandle2($mh)
    {
        $i = 0;
        $running = null;
        do {
            curl_multi_exec($mh, $running);
            $i++;
        } while ($running > 0);
        //var_dump($i);
    }

    //应该用这样的写法
    private function execMulitHandle($mh)
    {
        $i = 0;
        do {$mrc = curl_multi_exec($mh,$active); $i++;} while ($mrc == CURLM_CALL_MULTI_PERFORM);
        while ($active && $mrc == CURLM_OK)
        {
            if (curl_multi_select($mh) != -1)
            {
                do {$mrc = curl_multi_exec($mh, $active); $i++;} while ($mrc == CURLM_CALL_MULTI_PERFORM);
            }
            $i++;
        }
        //var_dump($i);
    }
}

看最后一个注释最多的函数,这个错误在平时调试的时候可能不太容易发现,因为程序完全正常,但是,在生产服务器下,马上会引起崩溃效果。

解释为什么不能这样,必须从C 语言内部实现的角度来分析。这个部分将放到下一个教程(PHP高级编程之–单线程实现并行抓取网页 )。不过不是通过C语言来表述原理,而是通过PHP

这个类,实际上也就很简单的实现了前面我们费了4个教程的篇幅,并且是九牛二虎之力才实现的多线程的抓取网页的功能。在纯PHP的实现下,我们只能用一个后台服务的方式来比较好的实现,但是当你使用 操作系统接口语言 C 语言时候,这个实现当然就更加的简单,灵活,高效。

就同时抓取几个网页这样一件简单的事情,实际上在底层涉及到了很多东西,对很多半路出家的PHP程序员,可能不喜欢谈多线程这个东西,深入了就涉及到操作系统,浅点说就是并行运行好几个“程序”。但是,很多时候,多线程必不可少,比如要写个快点的爬虫,往往就会浪费九牛二虎之力。不过,PHP的程序员现在应该感谢CURL 这个扩展,这样,你完全不需要用你不太精通的 python 去写爬虫了,对于一个中型大小的爬虫,有这个内部多线程,就已经足够了。

最后是上面的类的一个测试的例子:

$urls = array("http://baidu.com", "http://baidu.com", "http://baidu.com", "http://baidu.com", "http://baidu.com", "http://baidu.com", "http://www.google.com", "http://www.sina.com.cn", );
$m = new Http_MultiRequest();

$t = microtime(true);
$m->setUrls($urls);

//parallel fetch(并行抓取):
$data = $m->exec();
$parallel_time = microtime(true) - $t;
echo $parallel_time . "\n";

$t = microtime(true);

//serial fetch(串行抓取):
foreach ($urls as $url)
{
    $data[] = $m->execOne($url);
}
$serial_time = microtime(true) - $t;
echo $serial_time . "\n";
时间: 2024-10-18 14:11:09

PHP多进程(4) :内部多进程的相关文章

python:多进程,多进程队列,多进程管道,Manager,进程锁,进程池

#!usr/bin/env python# -*- coding:utf-8 -*- __author__ = "Samson" import multiprocessingimport time def run(name): time.sleep(2) print("process start...%s" % name)if __name__ == "__main__": for i in range(10): p = multiprocess

Android开发中怎样用多进程、用多进程的好处、多进程的缺陷、解决方法(转)

转自:http://blog.csdn.net/spencer_hale/article/details/54968092 1.怎样用多进程 Android多进程概念:一般情况下,一个应用程序就是一个进程,这个进程名称就是应用程序包名.我们知道进程是系统分配资源和调度的基本单位,所以每个进程都有自己独立的 资源和内存空间,别的进程是不能任意访问其他进程的内存和资源的. 如何让自己的应用拥有多个进程: 四大组件在AndroidManifest文件中注册的时候,有个属性android:process

PHP多进程初探 --- 利用多进程开发点儿东西吧

[原文地址:https://blog.ti-node.com/blog...] 干巴巴地叨逼叨了这么久,时候表演真正的技术了! 做个高端点儿的玩意吧,加入我们要做一个任务系统,这个系统可以在后台帮我们完成一大波(注意是一大波)数据的处理,那么我们自然想到,多开几个进程分开处理这些数据,同时我们不能执行了php task.php后终端挂起,万一一不小心关闭了终端都会导致任务失败,所以我们还要实现程序的daemon化.好啦,开始了! 首先,我们第一步就得将程序daemon化了! // 设置umask

Cpython解释器下实现并发编程——多进程、多线程、协程、IO模型

一.背景知识 进程即正在执行的一个过程.进程是对正在运行的程序的一个抽象. 进程的概念起源于操作系统,是操作系统最核心的概念,也是操作系统提供的最古老也是最重要的抽象概念之一.操作系统的其他所有内容都是围绕进程的概念展开的.   一.操作系统相关的知识 详情见链接:http://www.cnblogs.com/linhaifeng/p/6295875.html 即使可以利用的CPU只有一个(早期的计算机确实如此),也能保证支持(伪)并发的能力.将一个单独的CPU变成多个虚拟的CPU(多道技术:时

多进程浏览器、多线程页面渲染与js的单线程

线程与进程 说到单线程,就得从操作系统进程开始说起.在早期的操作系统中并没有线程的概念,进程是能拥有资源和独立运行的最小单位,也是程序执行的最小单位.任务调度采用的是时间片轮转的抢占式调度方式,而进程是任务调度的最小单位,每个进程有各自独立的一块内存,使得各个进程之间内存地址相互隔离.后来,随着计算机的发展,对CPU的要求越来越高,进程之间的切换开销较大,已经无法满足越来越复杂的程序的要求了.于是就发明了线程,线程是程序执行中一个单一的顺序控制流程,是程序执行流的最小单元.这里把线程比喻一个车间

Python多线程与多进程(一)

多线程 多线程是程序在同样的上下文中同时运行多条线程的能力.这些线程共享同一个进程的资源,可以在并发模式(单核处理器)或并行模式(多核处理器)下执行多个任务 多线程有以下几个优点: 持续响应:在单线程的程序中,执行一个长期运行的任务可能会导致程序的冻结.多线程可以把这个长期运行的任务放在一个线程中,在程序并发的运行任务时可以持续响应客户的需求 更快的执行速度:在多核处理器的操作系统上,多线程可以通过真正的并行提高程序的运行速度 较低的资源消耗:利用线程模式,程序可以利用一个进程内的资源响应多个请

多进程(multiprocessing module)

一.多进程 1.1 多进程的概念 由于GIL的存在,python中的多线程其实并不是真正的多线程,如果想要充分地使用多核CPU的资源,在python中大部分情况需要使用多进程.Python提供了非常好用的多进程包multiprocessing,只需要定义一个函数,Python会完成其他所有事情.借助这个包,可以轻松完成从单进程到并发执行的转换.multiprocessing支持子进程.通信和共享数据.执行不同形式的同步,提供了Process.Queue.Pipe.Lock等组件. multipr

第二章IPC——IPC与开启多进程

问题 一.IPC ①.什么是IPC  ②.为什么要有IPC 二.多进程 ①.如何开启多进程  ②.系统如何创建多进程 三.多进程引发的问题 问:私有进程(利用":+进程名")能否共享数据? 只能够共享data信息和组件信息...不能共享数据.

多进程vs多线程

多进程模式最大的优点就是稳定性高,因为一个子进程崩溃了,不会影响主进程和其他子进程.(当然主进程挂了所有进程就全挂了,但是Master进程只负责分配任务,挂掉的概率低)著名的Apache最早就是采用多进程模式. 多进程模式的缺点是创建进程的代价大,在Unix/Linux系统下,用fork调用还行,在Windows下创建进程开销巨大.另外,操作系统能同时运行的进程数也是有限的,在内存和CPU的限制下,如果有几千个进程同时运行,操作系统连调度都会成问题. 多线程模式通常比多进程快一点,但是也快不到哪