Python的多线程与多进程
多进程与多线程如何选择
- 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")
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()
多进程
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())
线程和进程的通信/同步方式
-
进程:
-
通信方式:
管道,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()
线程池
线程池使用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()
下一篇: jQuery操作文档-20130624
推荐阅读
-
python中多进程和进程池(Processing库)的实例代码
-
php多线程pthreads的安装与使用,php多线程pthreads
-
ubuntu下安装Python多版本的方法及注意事项
-
python 多线程与多进程效率测试
-
Python cookbook(数据结构与算法)从任意长度的可迭代对象中分解元素操作示例
-
python__基础 : 类的__new__方法与实现一个单例
-
Mybatis动态sql、if与where的使用、sql片段、foreach遍历、Mybatis的关联查询一对一、一对多、多对多、Mybatis的延时加载
-
python开启多个子进程并行运行的方法
-
Python的索引与切片原来该这样理解
-
CentOS下与Apache连接的PHP多版本共存方案实现详解,_PHP教程