程序员最近都爱上了这个网站  程序员们快来瞅瞅吧!  it98k网:it98k.com

本站消息

站长简介/公众号

  出租广告位,需要合作请联系站长

+关注
已关注

分类  

python面试大全(3)

python前言(0)

标签  

python面试(2)

python(0)

日期归档  

Python的多线程与多进程

发布于2019-11-17 17:04     阅读(1307)     评论(0)     点赞(24)     收藏(5)


多进程与多线程如何选择

  • 1、在io密集型操作使用多线程
  • 2、在cpu密集型操作使用多进程

多线程

Python中由于全局解释器锁GIL的存在,所以Python解释器同一时间内其实只能运行一个线程的代码。在多线程的情况下,只有当线程取得全局锁的时候,该线程的代码才会执行,而全局锁只有一个,所以即使使用多线程,同一时间内也只有一个线程在运行,那么线程如何调度呢?

多线程的线程切换

  • 1、有io操作的时候,当一个线程在做io操作的时候,因为io操作不需要cpu,所以会释放全局锁,线程会进行切换
  • 2、对于cpu密集型线程,Python中会有一个执行指令的计数器,当一个线程执行了一定数量的指令时,该线程会停止执行并释放锁,这时会进行线程切换

Python中创建线程:

实例化一个threading.Thread的对象,并传入一个初始化函数对象作为线程执行的入口;我们可以通过join方法让主线程阻塞,等待其创建的线程执行完成。

import threading
import time

def tstart(arg):
    time.sleep(0.5)
    print("%s running...." % arg)

if __name__ == '__main__':
    t1 = threading.Thread(target=tstart, args=('This is thread 1',))
    t2 = threading.Thread(target=tstart, args=('This is thread 2',))
    t1.start()
    t1.join() # 当前主线程阻塞,等待t1线程执行完成
    t2.start()
    t2.join() # 当前主线程阻塞,等待t2线程执行完成
    print("This is main function")
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

threading.Thread提供的线程对象方法和属性:

start():创建线程后通过start启动线程,等待CPU调度,为run函数执行做准备;

run():线程开始执行的入口函数,函数体中会调用用户编写的target函数,或者执行被重载的run函数;

join([timeout]):阻塞挂起调用该函数的线程,直到被调用线程执行完成或超时。通常会在主线程中调用该方法,等待其他线程执行完成。

name、getName()&setName():线程名称相关的操作;

ident:整数类型的线程标识符,线程开始执行前(调用start之前)为None;

isAlive()、is_alive():start函数执行之后到run函数执行完之前都为True;

daemon、isDaemon()&setDaemon():守护线程相关;

Python中的queue

queue模块中提供了同步的、线程安全的队列类,包括先进先出队列 Queue,后进先出队列LifoQueue,和优先级队列PriorityQueue。这些队列都实现了锁原语,可以直接使用来实现线程间的同步。

import threading,time

import queue

q = queue.Queue(maxsize=5)
#q = queue.LifoQueue(maxsize=3)
#q = queue.PriorityQueue(maxsize=3)

def ProducerA():
    count = 1
    while True:
        q.put(f"冷饮 {count}")
        print(f"A 放入:[冷饮 {count}]")
        count +=1
        time.sleep(1)

def  ConsumerB():
    while True:
        print(f"B 取出 [{q.get()}]")
        time.sleep(5)

p = threading.Thread(target=ProducerA)
c = threading.Thread(target=ConsumerB)
c.start()
p.start()

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26

多进程

Pyhton中创建进程

Python提供multiprocessing模块用于创建多进程,创建进程的方式和创建线程的方式类似,实例化一个multiprocessing.Process的对象,并传入一个初始化函数对象(initial function)作为新建进程执行入口;

from multiprocessing import Process  
import os, time

def pstart(name):
    print("Process name: %s, pid: %s "%(name, os.getpid()))

if __name__ == "__main__": 
    subproc = Process(target=pstart, args=('subprocess',))  
    subproc.start()  
    subproc.join()
    print("subprocess pid: %s"%subproc.pid)
    print("current process pid: %s" % os.getpid())
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

线程和进程的通信/同步方式

  • 进程
    • 通信方式
      管道,FIFO,消息队列,信号,共享内存,socket,stream流;
    • 同步方式:PV信号量,管程
  • 线程
    • 同步方式:互斥锁,递归锁,条件变量,信号量
    • 通信方式:位于同一进程的线程共享进程资源,因此线程间没有类似于进程间用于数据传递的通信方式,线程间的通信主要是用于线程同步。

进程池与线程池

python中线程池与进程池使用的同一模块 multiprocessing

进程池

from multiprocessing import Pool 这样导入的Pool表示的是进程池。

进程池apply与apply_async的区别

Pyhton官方推荐使用apply_async

  • apply方法是阻塞的
  • apply_async是异步非阻塞的

爬虫进程池实例:


import requests
import random
from multiprocessing import Pool
import time
import os

# 返回一个随机的请求头 headers
def getheaders():
    user_agent_list = [ \
        "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.1 (KHTML, like Gecko) Chrome/22.0.1207.1 Safari/537.1" \
        "Mozilla/5.0 (X11; CrOS i686 2268.111.0) AppleWebKit/536.11 (KHTML, like Gecko) Chrome/20.0.1132.57 Safari/536.11", \
        "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.6 (KHTML, like Gecko) Chrome/20.0.1092.0 Safari/536.6", \
        "Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.6 (KHTML, like Gecko) Chrome/20.0.1090.0 Safari/536.6", \
        "Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.1 (KHTML, like Gecko) Chrome/19.77.34.5 Safari/537.1", \
        "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/536.5 (KHTML, like Gecko) Chrome/19.0.1084.9 Safari/536.5", \
        "Mozilla/5.0 (Windows NT 6.0) AppleWebKit/536.5 (KHTML, like Gecko) Chrome/19.0.1084.36 Safari/536.5", \
        "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1063.0 Safari/536.3", \
        "Mozilla/5.0 (Windows NT 5.1) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1063.0 Safari/536.3", \
        "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_0) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1063.0 Safari/536.3", \
        "Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1062.0 Safari/536.3", \
        "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1062.0 Safari/536.3", \
        "Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1061.1 Safari/536.3", \
        "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1061.1 Safari/536.3", \
        "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1061.1 Safari/536.3", \
        "Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1061.0 Safari/536.3", \
        "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/535.24 (KHTML, like Gecko) Chrome/19.0.1055.1 Safari/535.24", \
        "Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/535.24 (KHTML, like Gecko) Chrome/19.0.1055.1 Safari/535.24"
    ]
    user_agent = random.choice(user_agent_list)
    headers = {'User-Agent': user_agent}
    return headers


# 检查ip是否可用
def checkip(ip):
    headers = getheaders()  # 定制请求头
    proxies = {"http": "http://" + ip, "https": "https://" + ip}  # 代理ip
    try:
        response = requests.get(url='http://icanhazip.com', proxies=proxies, headers=headers, timeout=3)
        if response.status_code == 200:
            return True
        else:
            return False
    except:
        return False


def task(name):
    result = checkip("117.30.113.134:9999")
    # print ('Run task %s (%s)...'%(name,os.getpid()))
    # print (time.time())
    time.sleep(2)


if __name__ == '__main__':
    # print ('Parent process %s'%os.getpid())
    p = Pool()
    for i in range(4):
        p.apply_async(task, args=(i,))
    # print ('Waiting for all subprocess done ...')
    p.close()
    p.join()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63

线程池

线程池使用Pyhton3自带的concurrent.futures模块。

concurrent.futures.ThreadPoolExecutor,在提交任务的时候,有两种方式,一种是submit函数,另一种是map函数,两者的主要区别在于:

  • map可以保证输出的顺序,submit输出的顺序是乱的
  • 如果你要提交的任务的函数是一样的,就可以简化成map。但是假如提交的任务函数是不一样的,或者执行的过程之可能出现异常(使用map执行过程中发现问题会直接抛出错误)就要用到submit

线程池实例

#! /usr/bin/env python
# -*- coding: utf-8 -*-

from concurrent.futures import ThreadPoolExecutor
import time

def sayhello(a):
    print("hello: "+a)
    time.sleep(2)

def main():
    seed=["a","b","c"]
    start1=time.time()
    for each in seed:
        sayhello(each)
    end1=time.time()
    print("time1: "+str(end1-start1))
    start2=time.time()
    with ThreadPoolExecutor(3) as executor:
        for each in seed:
            executor.submit(sayhello,each)
    end2=time.time()
    print("time2: "+str(end2-start2))
    start3=time.time()
    with ThreadPoolExecutor(3) as executor1:
        executor1.map(sayhello,seed)
    end3=time.time()
    print("time3: "+str(end3-start3))

if __name__ == '__main__':
    main()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31


所属网站分类: 技术文章 > 博客

作者:骷髅无悔

链接:https://www.pythonheidong.com/blog/article/158345/80b393661c2c159c7bf3/

来源:python黑洞网

任何形式的转载都请注明出处,如有侵权 一经发现 必将追究其法律责任

24 0
收藏该文
已收藏

评论内容:(最多支持255个字符)