进程

参考
http://www.cnblogs.com/Eva-J/articles/8253549.html
http://www.cnblogs.com/clschao/articles/9629392.html

进程、multiprocess模块

multiprocess模块,大致分为四个部分:创建进程部分,进程同步部分,进程池部分,进程之间数据共享。

multiprocess.Process模块

Process([group [, target [, name [, args [, kwargs]]]]]),由该类实例化得到的对象,表示一个子进程中的任务(尚未启动)

强调

  1. 需要使用关键字的方式来指定参数
  2. args指定的为传给target函数的位置参数,是一个元组形式,必须有逗号

参数介绍

group参数未使用,值始终为None
target表示调用对象,即子进程要执行的任务
args表示调用对象的位置参数元组,args=(1,2,'egon',)
kwargs表示调用对象的字典,kwargs={'name':'egon','age':18}
name为子进程的名称

方法介绍

p.start():启动进程,并调用该子进程中的p.run()
p.run():进程启动时运行的方法,正是它去调用target指定的函数,我们自定义类的类中一定要实现该方法
p.terminate():强制终止进程p,不会进行任何清理操作,如果p创建了子进程,该子进程就成了僵尸进程,使用该方法需要特别小心这种情况。如果p还保存了一个锁那么也将不会被释放,进而导致死锁
p.is_alive():如果p仍然运行,返回True
p.join([timeout]):主线程等待p终止(强调:是主线程处于等的状态,而p是处于运行的状态)。timeout是可选的超时时间,需要强调的是,p.join只能join住start开启的进程,而不能join住run开启的进程

属性介绍

p.daemon:默认值为False,如果设为True,代表p为后台运行的守护进程,当p的父进程终止时,p也随之终止,并且设定为True后,p不能创建自己的新进程,必须在p.start()之前设置
p.name:进程的名称
p.pid:进程的pid
p.exitcode:进程在运行时为None、如果为–N,表示被信号N结束(了解即可)
p.authkey:进程的身份验证键,默认是由os.urandom()随机生成的32字符的字符串。这个键的用途是为涉及网络连接的底层进程间通信提供安全性,这类连接只有在具有相同的身份验证键时才能成功(了解即可)

在Windows操作系统中使用注意事项
在Windows操作系统中由于没有fork(linux操作系统中创建进程的机制),在创建子进程的时候会自动 import 启动它的这个文件,而在 import 的时候又执行了整个文件。因此如果将process()直接写在文件中就会无限递归创建子进程报错。所以必须把创建子进程的部分使用if name ==‘main’ 判断保护起来,import 的时候 ,就不会递归运行了。

Process 创建进程

两种方式

  1. 调用Process()
  2. 继承Process(),重写run方法,在__init__方法中调用父类init方法

创建进程的两种方式

# 第一种,直接调用Process()
from multiprocessing import Process
import time
import os


def func(num):
    print(num)
    print('子进程id:', os.getpid())
    print('父进程id:', os.getppid())


if __name__ == '__main__':
    for i in range(5):
        p = Process(target=func, args=(i,))
        p.start()

    time.sleep(3)
    print('父进程结束')

# 第二种,类的方式,继承Process,重写run方法,如果传参数,在init方法中调用父类init方法
from multiprocessing import Process
import os


class MyProcess(Process):

    def __init__(self, num, name):
        super().__init__()
        self.num = num
        self.name = name

    def run(self):
        print(self.num, self.name)
        print('子进程id:', os.getpid())
        print('父进程id:', os.getppid())


if __name__ == '__main__':
    p_list = []
    for i in range(5):
        p = MyProcess(i, 'robot {}'.format(i))
        p_list.append(p)
    for i in p_list:
        i.start()
    print('父进程结束')

守护进程

会随着主进程的结束而结束。
注意

  1. 守护进程会在主进程代码执行结束后就终止
  2. 守护进程内无法再开启子进程,否则抛出异常:AssertionError: daemonic processes are not allowed to have children

守护进程

import time
import os
from multiprocessing import Process


def func():
    time.sleep(5)
    print(os.getpid())
    print('子进程')


if __name__ == '__main__':
    p1 = Process(target=func, )
    # 将p1子进程设置为守护进程
    p1.daemon = True
    p1.start()
    print('主进程结束')

僵尸进程与孤儿进程

参考 http://www.cnblogs.com/Anker/p/3271773.html
背景介绍
我们知道在unix/linux中,正常情况下,子进程是通过父进程创建的,子进程在创建新的进程。子进程的结束和父进程的运行是一个异步过程,即父进程永远无法预测子进程 到底什么时候结束。 当一个 进程完成它的工作终止之后,它的父进程需要调用wait()或者waitpid()系统调用取得子进程的终止状态。
孤儿进程:一个父进程退出,而它的一个或多个子进程还在运行,那么那些子进程将成为孤儿进程。孤儿进程将被init进程(进程号为1)所收养,并由init进程对它们完成状态收集工作。
僵尸进程:一个进程使用fork创建子进程,如果子进程退出,而父进程并没有调用wait或waitpid获取子进程的状态信息,那么子进程的进程描述符仍然保存在系统中。这种进程称之为僵死进程。

危害
僵尸进程会占用描述符(进程基本信息),而导致进程部分资源无法释放。
任何进程(init除外)结束后,并非马上消失,而是留下一个僵尸进程的数据结构,等待父进程处理。

进程同步multiprocess.Lock,multiprocess.Semaphore,multiprocess.Event

锁(互斥信号量) —— multiprocess.Lock

当多个进程使用同一份数据资源的时候,就会引发数据安全或顺序混乱问题。

multiprocess.Lock示例

from multiprocessing import Process, Lock
import time


def buy_ticket(lock):
    lock.acquire()
    with open('test', 'r+') as f:
        num = int(f.read())
        if num > 0:
            num -= 1
            f.seek(0)
            f.write(str(num))
            print('购票成功')
        else:
            print('购票失败')
        time.sleep(0.2)
    lock.release()


if __name__ == '__main__':
    lock = Lock()
    for i in range(5):
        p = Process(target=buy_ticket, args=(lock,))
        p.start()
    time.sleep(3)
    print('end')


信号量 —— multiprocess.Semaphore

互斥锁同时只允许一个线程更改数据,而信号量Semaphore是同时允许一定数量的线程更改数据。
实现
信号量同步基于内部计数器,每调用一次acquire(),计数器减1;每调用一次release(),计数器加1.当计数器为0时,acquire()调用被阻塞。这是迪科斯彻(Dijkstra)信号量概念P()和V()的Python实现。信号量同步机制适用于访问像服务器这样的有限资源。
信号量与进程池的概念很像,但是要区分开,信号量涉及到加锁的概念
multiprocess.Semaphore示例

from multiprocessing import Process, Semaphore
import time
import random


def go_ktv(sem, user_name):
    """
    一个 ktv 最多X个人,满了进不去,等着。
    :param sem: Semaphore
    :param user_name: str
    :return: None
    """
    sem.acquire()
    print('number {} sing'.format(user_name))
    time.sleep(random.randrange(0, 3))
    sem.release()


if __name__ == '__main__':
    sem = Semaphore(3)
    for i in range(10):
        p = Process(target=go_ktv, args=(sem, str(i)))
        p.start()
    # print('End')

事件 —— multiprocess.Event

python线程的事件用于主线程控制其他线程的执行,事件主要提供了三个方法 set、wait、clear。
事件处理的机制:全局定义了一个“Flag”,如果“Flag”值为 False,那么当程序执行 event.wait 方法时就会阻塞,如果“Flag”值为True,那么event.wait 方法时便不再阻塞。
clear:将“Flag”设置为False
set:将“Flag”设置为True

multiprocess.Event示例

from multiprocessing import Process, Event
import time


# Event
# is_set()  set() clear() wait()
# 红绿灯模拟

def car(event, car_id):
    if not event.is_set():
        print('红灯,等着。')
        event.wait()
    print('绿灯,开车 {}'.format(car_id))


def pedestrian(event, pedestrian_id):
    if not event.is_set():
        print('红灯,等着。')
        event.wait()
    print('绿灯,走 {}'.format(pedestrian_id))


def traffic_lights(event: Event, interval: int) -> None:
    """
    交通灯,每隔interval秒变灯,只有红绿的。
    :param event:
    :param interval:
    :return:
    """
    while True:
        time.sleep(interval)
        if event.is_set():
            event.clear()
            print('绿灯')
        else:
            event.set()
            print('红灯')


if __name__ == '__main__':
    event = Event()
    for i in range(10):
        # 10辆车
        p = Process(target=car, args=(event, str(i)))
        p.start()
    for i in range(5):
        # 5个人
        p = Process(target=pedestrian, args=(event, str(i),))
        p.start()

    p = Process(target=traffic_lights, args=(event, 3))
    p.start()

进程间通信-队列和管道(multiprocess.Queue,multiprocess.Pipe)

队列

创建共享的进程队列,Queue是多进程安全的队列,可以使用Queue实现多进程之间的数据传递。
队列底层使用管道和锁定实现

Queue([maxsize])
创建共享的进程队列。maxsize是队列中允许的最大项数。如果省略此参数,则无大小限制。底层队列使用管道和锁定实现。另外,还需要运行支持线程以便队列中的数据传输到底层管道中。
Queue的实例q具有以下方法:

q.get( [ block [ ,timeout ] ] )
返回q中的一个项目。如果q为空,此方法将阻塞,直到队列中有项目可用为止。block用于控制阻塞行为,默认为True. 如果设置为False,将引发Queue.Empty异常(定义在Queue模块中)。timeout是可选超时时间,用在阻塞模式中。如果在制定的时间间隔内没有项目变为可用,将引发Queue.Empty异常。

q.get_nowait( )
同q.get(False)方法。

q.put(item [, block [,timeout ] ] )
将item放入队列。如果队列已满,此方法将阻塞至有空间可用为止。block控制阻塞行为,默认为True。如果设置为False,将引发Queue.Empty异常(定义在Queue库模块中)。timeout指定在阻塞模式中等待可用空间的时间长短。超时后将引发Queue.Full异常。

q.qsize()
返回队列中目前项目的正确数量。此函数的结果并不可靠,因为在返回结果和在稍后程序中使用结果之间,队列中可能添加或删除了项目。在某些系统上,此方法可能引发NotImplementedError异常。

q.empty()
如果调用此方法时 q为空,返回True。如果其他进程或线程正在往队列中添加项目,结果是不可靠的。也就是说,在返回和使用结果之间,队列中可能已经加入新的项目。

q.full()
如果q已满,返回为True. 由于线程的存在,结果也可能是不可靠的(参考q.empty()方法)。。

JoinableQueue([maxsize])
创建可连接的共享进程队列。这就像是一个Queue对象,但队列允许项目的使用者通知生产者项目已经被成功处理。通知进程是使用共享的信号和条件变量来实现的。
参数介绍:
JoinableQueue的实例p除了与Queue对象相同的方法之外,还具有以下方法:
q.task_done()
使用者使用此方法发出信号,表示q.get()返回的项目已经被处理。如果调用此方法的次数大于从队列中删除的项目数量,将引发ValueError异常。
q.join()
生产者将使用此方法进行阻塞,直到队列中所有项目均被处理。阻塞将持续到为队列中的每个项目均调用q.task_done()方法为止。

生产者消费者模型
生产者消费者模式是通过一个容器来解决生产者和消费者的强耦合问题。生产者和消费者彼此之间不直接通讯,而通过阻塞队列来进行通讯,所以生产者生产完数据之后不用等待消费者处理,直接扔给阻塞队列,消费者不找生产者要数据,而是直接从阻塞队列里取,阻塞队列就相当于一个缓冲区,平衡了生产者和消费者的处理能力。

基于Queue实现生产者消费者模型

from multiprocessing import Process, Queue
import time
import os
import random


def consumer(queue: Queue):
    while True:
        time.sleep(random.randint(1, 3))
        res = queue.get()
        print('{}\t吃\t{}'.format(os.getpid(), res))


def producer(queue: Queue):
    for i in range(10):
        time.sleep(random.randint(1, 3))
        res = '包子{}'.format(i)
        queue.put(res)
        print('{}\t生产\t{}'.format(os.getpid(), res))


if __name__ == '__main__':
    q = Queue(3)
    producer_process = Process(target=producer, args=(q,))
    consumer_process = Process(target=consumer, args=(q,))
    producer_process.start()
    consumer_process.start()
    print('主进程')

"""
此时的问题是主进程永远不会结束,原因是:生产者p在生产完后就结束了,但是消费者c在取空了q之后,则一直处于死循环中且卡在q.get()这一步。
解决方式无非是让生产者在生产完毕后,往队列中再发一个结束信号,这样消费者在接收到结束信号后就可以break出死循环
"""

基于Queue实现生产者消费者模型(多个消费者生产者)

from multiprocessing import Process, Queue
import time
import os
import random


def consumer(queue: Queue):
    while True:
        time.sleep(random.randint(1, 3))
        res = queue.get()
        if not res:
            break
        print('{}\t吃\t{}'.format(os.getpid(), res))


def producer(queue: Queue):
    for i in range(10):
        time.sleep(random.randint(1, 3))
        res = '包子{}'.format(i)
        queue.put(res)
        print('{}\t生产\t{}'.format(os.getpid(), res))


if __name__ == '__main__':
    q = Queue(3)
    producer_process_list = []
    consumer_process_list = []
    for i in range(3):
        producer_process = Process(target=producer, args=(q,))
        producer_process.start()
        producer_process_list.append(producer_process)

    for i in range(10):
        consumer_process = Process(target=consumer, args=(q,))
        consumer_process.start()
        consumer_process_list.append(consumer_process)
    for i in producer_process_list:
        i.join()
    for i in range(len(consumer_process_list)):
        q.put(None)

    print('主进程')

基于JoinableQueue实现生产者消费者模型(多个消费者生产者)

# JoinableQueue的实例p除了与Queue对象相同的方法之外,还具有以下方法:
# q.task_done()
# 消费者使用此方法发出信号,表示q.get()返回的项目已经被处理。如果调用此方法的次数大于从队列中删除的项目数量,将引发ValueError异常。
# q.join()
# 生产者将使用此方法进行阻塞,直到队列中所有项目均被处理。阻塞将持续到为队列中的每个项目均调用q.task_done()方法为止。

# 主进程里面,设置消费者为守护进程,生产者join,当生产者结束,主进程结束,守护进程消费者结束。
# 下面的例子说明如何建立永远运行的进程,使用和处理队列上的项目。生产者将项目放入队列,并等待它们被处理。


from multiprocessing import Process, JoinableQueue
import time
import random
import os


def consumer(queue: JoinableQueue):
    while True:
        time.sleep(random.randint(1, 3))
        res = queue.get()
        print('{}\t吃\t{}'.format(os.getpid(), res))
        queue.task_done()  # #向q.join()发送一次信号,证明一个数据已经被取走了


def producer(queue: JoinableQueue):
    for i in range(10):
        time.sleep(random.randint(1, 3))
        res = '包子{}'.format(i)
        queue.put(res)
        print('{}\t生产\t{}'.format(os.getpid(), res))
    queue.join()  # 生产完毕,使用此方法进行阻塞,直到队列中所有项目均被处理。


if __name__ == '__main__':
    q = JoinableQueue(10)
    producer_list = []
    consumer_list = []
    # 生产者
    for i in range(10):
        p = Process(target=producer, args=(q,))
        producer_list.append(p)
    # 消费者
    for i in range(10):
        p = Process(target=consumer, args=(q,))
        p.daemon = True
        consumer_list.append(p)

    for i in producer_list:
        i.start()

    for i in consumer_list:
        i.start()

    for i in producer_list:
        i.join()

    print('主进程结束')
# 主进程等生产者,生产者等消费者,
# 消费者获取完毕,生产者结束,主进程结束,消费者结束。

管道PIPE

创建管道的类:
Pipe([duplex]):在进程之间创建一条管道,并返回元组(conn1,conn2),其中conn1,conn2表示管道两端的连接对象,强调一点:必须在产生Process对象之前产生管道

参数介绍
dumplex:默认管道是全双工的,如果将duplex射成False,conn1只能用于接收,conn2只能用于发送。

主要方法:
conn1.recv():接收conn2.send(obj)发送的对象。如果没有消息可接收,recv方法会一直阻塞。如果连接的另外一端已经关闭,那么recv方法会抛出EOFError。
conn1.send(obj):通过连接发送对象。obj是与序列化兼容的任意对象

其他方法
conn1.close():关闭连接。如果conn1被垃圾回收,将自动调用此方法
conn1.fileno():返回连接使用的整数文件描述符
conn1.poll([timeout]):如果连接上的数据可用,返回True。timeout指定等待的最长时限。如果省略此参数,方法将立即返回结果。如果将timeout射成None,操作将无限期地等待数据到达。
conn1.recv_bytes([maxlength]):接收c.send_bytes()方法发送的一条完整的字节消息。maxlength指定要接收的最大字节数。如果进入的消息,超过了这个最大值,将引发IOError异常,并且在连接上无法进行进一步读取。如果连接的另外一端已经关闭,再也不存在任何数据,将引发EOFError异常。
conn.send_bytes(buffer [, offset [, size]]):通过连接发送字节数据缓冲区,buffer是支持缓冲区接口的任意对象,offset是缓冲区中的字节偏移量,而size是要发送字节数。结果数据以单条消息的形式发出,然后调用c.recv_bytes()函数进行接收
conn1.recv_bytes_into(buffer [, offset]):接收一条完整的字节消息,并把它保存在buffer对象中,该对象支持可写入的缓冲区接口(即bytearray对象或类似的对象)。offset指定缓冲区中放置消息处的字节位移。返回值是收到的字节数。如果消息长度大于可用的缓冲区空间,将引发BufferTooShort异常。

multiprocessing.Pipe示例

from multiprocessing import Pipe, Process


def func(conn):
    msg = conn.recv()  # 阻塞
    print(msg)


if __name__ == '__main__':
    conn1, conn2 = Pipe()
    p = Process(target=func, args=(conn2,))
    p.start()
    conn1.send('2333')

进程间的数据共享

进程间数据是独立的,可以借助于队列或管道实现通信,二者都是基于消息传递的。
虽然进程间数据独立,但可以通过Manager实现数据共享,事实上Manager的功能远不止于此。
也可以通过数据库解决数据共享问题。

Manager
A manager object returned by Manager() controls a server process which holds Python objects and allows other processes to manipulate them using proxies.
A manager returned by Manager() will support types list, dict, Namespace, Lock, RLock, Semaphore, BoundedSemaphore, Condition, Event, Barrier, Queue, Value and Array.

数据共享示例

from multiprocessing import Manager, Process, Lock


def work(d, lock):
    with lock:  # 不加锁而操作共享的数据,肯定会出现数据错乱
        d['count'] -= 1
    # d['count'] -= 1


if __name__ == '__main__':
    lock = Lock()
    with Manager() as m:
        dic = m.dict({'count': 100})
        p_l = []
        for i in range(100):
            p = Process(target=work, args=(dic, lock))
            p_l.append(p)
            p.start()
        for p in p_l:
            p.join()
        print(dic)

进程池和multiprocess.Pool模块

在程序实际处理问题过程中,忙时会有成千上万的任务需要被执行,闲时可能只有零星任务。那么在成千上万个任务需要被执行的时候,我们就需要去创建成千上万个进程么?首先,创建进程需要消耗时间,销毁进程也需要消耗时间。第二即便开启了成千上万的进程,操作系统也不能让他们同时执行,这样反而会影响程序的效率。因此我们不能无限制的根据任务开启或者结束进程。那么我们要怎么做呢?
在这里,要给大家介绍一个进程池的概念,定义一个池子,在里面放上固定数量的进程,有需求来了,就拿一个池中的进程来处理任务,等到处理完毕,进程并不关闭,而是将进程再放回进程池中继续等待任务。如果有很多任务需要执行,池中的进程数量不够,任务就要等待之前的进程执行任务完毕归来,拿到空闲进程才能继续执行。也就是说,池中进程的数量是固定的,那么同一时间最多有固定数量的进程在运行。这样不会增加操作系统的调度难度,还节省了开闭进程的时间,也一定程度上能够实现并发效果。

概念介绍

Pool([numprocess [,initializer [, initargs]]]):创建进程池

参数介绍
numprocess:要创建的进程数,如果省略,将默认使用cpu_count()的值
initializer:是每个工作进程启动时要执行的可调用对象,默认为None
initargs:是要传给initializer的参数组

主要方法
p.apply(func [, args [, kwargs]]):在一个池工作进程中执行func(args,**kwargs),然后返回结果。
'''需要强调的是:此操作并不会在所有池工作进程中并执行func函数。如果要通过不同参数并发地执行func函数,必须从不同线程调用p.apply()函数或者使用p.apply_async()'''
p.apply_async(func [, args [, kwargs]]):在一个池工作进程中执行func(
args,**kwargs),然后返回结果。
'''此方法的结果是AsyncResult类的实例,callback是可调用对象,接收输入参数。当func的结果变为可用时,将理解传递给callback。callback禁止执行任何阻塞操作,否则将接收其他异步操作中的结果。'''
p.close():关闭进程池,防止进一步操作。如果所有操作持续挂起,它们将在工作进程终止前完成
P.jion():等待所有工作进程退出。此方法只能在close()或teminate()之后调用

其他方法
方法apply_async()和map_async()的返回值是AsyncResul的实例obj。实例具有以下方法
obj.get():返回结果,如果有必要则等待结果到达。timeout是可选的。如果在指定时间内还没有到达,将引发一场。如果远程操作中引发了异常,它将在调用此方法时再次被引发。
obj.ready():如果调用完成,返回True
obj.successful():如果调用完成且没有引发异常,返回True,如果在结果就绪之前调用此方法,引发异常
obj.wait([timeout]):等待结果变为可用。
obj.terminate():立即终止所有工作进程,同时不执行任何清理或结束任何挂起工作。如果p被垃圾回收,将自动调用此函数

回调函数

需要回调函数的场景:进程池中任何一个任务一旦处理完了,就立即告知主进程:我好了额,你可以处理我的结果了。主进程则调用一个函数去处理该结果,该函数即回调函数
我们可以把耗时间(阻塞)的任务放到进程池中,然后指定回调函数(主进程负责执行),这样主进程在执行回调函数时就省去了I/O的过程,直接拿到的是任务的结果。

回调函数示例

from multiprocessing import Pool
import os


def func1(n):
    print('fun1>>', os.getpid())
    return n ** 2


def func2(n):
    print('fun2>>', os.getpid())
    print(n)


if __name__ == '__main__':
    print('主进程>>', os.getpid())
    pool = Pool(4)
    pool.apply_async(func1, args=(10,), callback=func2)
    # fun1异步执行完毕后,将返回值交给fun2执行(在主进程中).
    # fun2的值参数只能由fun1传递,不能手动传值。
    # fun1的返回值是元组类型
    pool.close()
    pool.join()

方法示例

map方法

from multiprocessing import Pool, Process


def func(n):
    print(n)


if __name__ == '__main__':
    pool = Pool(4)
    pool.map(func, range(100))  # 传递参数必须是课迭代的

进程池map与多进程效率对比

from multiprocessing import Process, Pool
import time


def func(n):
    # time.sleep(0.1)
    print(n)


if __name__ == '__main__':
    pool = Pool(4)
    pool_start_time = time.time()
    pool.map(func, range(100))
    pool_end_time = time.time()

    mult_process_start_time = time.time()
    process_list = []
    for i in range(100):
        p = Process(target=func, args=(i,))
        p.start()
        process_list.append(p)
    for p in process_list:
        p.join()
    mult_process_end_time = time.time()

    print('进程池运行时间:', pool_end_time - pool_start_time)
    print('多进程运行时间:', mult_process_end_time - mult_process_start_time)

# map是异步执行的,自带close和join
# 进程池的效率高
# 进程池运行时间: 0.27078747749328613
# 多进程运行时间: 8.477996110916138

进程池的同步调用,apply()

from multiprocessing import Pool, Process
import time


def func(n):
    time.sleep(0.5)
    print(n)

    return n ** 2


if __name__ == '__main__':
    pool = Pool(4)
    for i in range(10):
        res = pool.apply(func, args=(i,))  # 同步执行,会等待返回参数
        print(res)

进程池的异步调用,apply_async()

from multiprocessing import Pool
import time


def func(n):
    time.sleep(0.5)
    # print(n)
    return n ** 2


if __name__ == '__main__':
    pool = Pool(4)
    res_list = []
    for i in range(10):
        res = pool.apply_async(func, args=(i,))  # 异步执行
        res_list.append(res)

    for i in res_list:
        print(i.get())  # 使用get来获取apply_aync的结果,如果是apply,则没有get方法,因为apply是同步执行,立刻获取结果,也根本无需get

进程池版socket并发聊天

# 服务端
from multiprocessing import Pool
import socket
import os


def talk(conn):
    print('子进程:', os.getpid())
    while True:
        try:
            msg = conn.recv(1024)
            if not msg:
                break
            conn.send(msg.decode('utf-8').upper().encode('utf-8'))
        except Exception:
            break


server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
server.bind(('127.0.0.1', 8800))
server.listen(5)

if __name__ == '__main__':
    pool = Pool(4)
    while True:
        conn, addr = server.accept()
        pool.apply_async(talk, args=(conn,))


# 客户端
import socket

client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client.connect(('127.0.0.1', 8800))

while True:
    msg = input('>>>').strip()
    if not msg:
        continue
    client.send(msg.encode('utf-8'))
    res = client.recv(1024)
    print(res.decode('utf-8'))

简单并发爬虫示例

from multiprocessing import Pool
import os
import re
import requests


def get_page(url, pattern):
    print('开始爬\t{},进程:\t{}'.format(url, os.getpid()))
    res = requests.get(url)
    res.encoding = 'utf-8'
    text = None
    if res.status_code == 200:
        text = res.text
    return text, pattern


def parser_page(args):
    res, pattern = args
    if not res:
        return
    parser_res = re.search(pattern, res)
    with open('test', 'a', encoding='utf-8') as f:
        if parser_res:
            f.write(parser_res.group()+'\n')


if __name__ == '__main__':
    urls = [
        'https://www.baidu.com',
        'https://www.python.org',
        'https://www.openstack.org',
        'https://help.github.com/',
        'http://www.sina.com.cn/'
    ]
    patterns = [
        r'<title>(.*?)</title>',
        r'<title>(.*?)</title>',
        r'<title>(.*?)</title>',
        r'<title>(.*?)</title>',
        r'<title>(.*?)</title>',
    ]
    pool = Pool(10)
    for key, url in enumerate(urls):
        pool.apply_async(get_page, args=(url, patterns[key],), callback=parser_page)

    pool.close()
    pool.join()
    print('爬虫结束')


posted @ 2018-12-05 21:52  写bug的日子  阅读(88)  评论(0编辑  收藏  举报