python之进程,线程,和协程
线程进程介绍
1. 工作最小单元是线程
2. 应用程序 -> 至少有一个进程 -> 至少有一个线程
3. 应用场景:
IO密集型:线程
计算密集型:进程
4. GIL,全局解释器锁。
- 保证同一个进程中只有一个线程同时被调度
线程进程的区别
1.进程 内存独立,线程共享同一进程的内存
2.进程是资源的集合, 线程是执行单位
3. 进程之间不能直接互相访问,线程可以互相通信
4. 创建新进程非常消耗系统资源, 线程非常轻量,只保存线程需要运行时的必要数据,如上下文,程序堆栈
5. 同一进程里的线程可以互相控制,父进程之可以控制子进程
CPU是如何处理线程的
当某个程序下,所有开启的线程准备就绪,此时CPU就会在这些线程之间不停的切换处理每个线程的逻辑,之后返回结果,对于人的肉眼而言,就是并发的效果
python的多进程,多线程分析
假如系统有两个核心,此时呢有一个应用进程,里面开启了三个线程,每个进程中有一个主线程和两个子线程,CPU在处理线程的时候会随机调用,所以单个进程中
的不同的线程,有可能会被两个核心在同一时刻处理,从而实现利用多核优势并发,但是在python中,因为GIL(全局解释器锁)的存在,同一时刻每个进程中只能有一
个线程被处理。假设此时有一个进程,这种情况下python就不能利用多核的优势,因为只允许一个线程被处理对不对。另一个核心就浪费了呀!所以对于python而言,
做IO密集型任务的时候,才适合用多线程,因为不依赖CPU,而对于计算密集型任务,就使用多进程。
建立http请求属于IO请求
python线程
创建
我们来创建一个线程玩玩,python中线程模块为threading
#_*_coding:utf-8_*_ __author__='wuzhihu' import threading 导入线程模块 import time def func(arg): 在这里我们首先创建一个函数来作为我们的任务 time.sleep(1) print(arg) for i in range(8): 利用for循环来同时开启多个线程 t=threading.Thread(target=func,args=[i,]) 创建一个线程 t.start() 启动线程,告诉CPU准备就绪,来调用我把 print('end') 主线程执行一个打印任务
然后执行看返回结果,可以看到主线程先走到末尾了,而且各个线程的完结也都不一样,这也证实了,CPU是随机处理各个线程的,线程在启动了之后就会等在那里
"C:\Program Files\Python35\python.exe" F:/oldboy/day9/线程.py end 4 3 2 5 1 0 6 7
指定主线程是否等待子线程
我们有时候呢可能会需要说,主线程结束的时候所有子线程也终止,我们来看看怎么做,如下添加一行,注意:必须加到start之前
#_*_coding:utf-8_*_
__author__='wuzhihu'
import threading
import time
def func(arg):
time.sleep(1)
print(arg)
for i in range(8):
t=threading.Thread(target=func,args=[i,])
t.setDaemon(True)
t.start()
print('end')
执行结果
"C:\Program Files\Python35\python.exe" F:/oldboy/day9/线程.py end
所以呢,这里如果不指定setDaemon为True的话,他默认为False
设定下个线程执行时等待上个线程的时间
1 #_*_coding:utf-8_*_ 2 __author__='wuzhihu' 3 4 import threading 5 6 import time 7 8 def func(arg): 9 time.sleep(3) 10 print(arg) 11 12 for i in range(8): 13 t=threading.Thread(target=func,args=[i,]) 14 t.setDaemon(True) 15 t.start() 16 t.join(1) 指定等待的时间,默认为一直等待,知道上个线程执行完成,在执行下个线程17 18 print('end')
线程里面的函数是如何调用的
可以看出来,是通过threading这个类中的run方法调用的
class Mythread(threading.Thread):
def __init__(self,func,*args,**kwargs):
super(Mythread,self).__init__(*args,**kwargs)
self.func=func
def run(self):
self.func('hahaha')
def func(arg):
time.sleep(1)
print(arg)
obj=Mythread(func=func)
obj.start()
运行结果
"C:\Program Files\Python35\python.exe" F:/oldboy/day9/线程.py
hahaha
线程锁
有时候呢,我们会遇到某些操作不允许并发的情况,这时候就需要用到锁
允许一个线程进出
import threading import time v=10 # lock=threading.Lock() #创建一把锁 lock=threading.RLock() ##可以递归解锁 def func(arg): time.sleep(2) lock.acquire() #锁住,从此处开始只允许进入一个线程 lock.acquire() ##双重锁 global v v-=1 print(v) lock.release() #解锁 lock.release() #上几把锁,就解几把 for i in range(10): t=threading.Thread(target=func,args=[i,]) t.start() print('end')
运行结果:
"C:\Program Files\Python35\python.exe" F:/oldboy/day9/线程锁.py
end
9
8
7
6
5
4
3
2
1
0
多个人同时使用锁
只需将上面的创建锁换成下面的方式即可,
lock=threading.BoundedSemaphore(3) 3表示允许三个线程同时进去
事件锁
import threading import time lock=threading.Event() 创建事件锁 def func(arg): time.sleep(2) lock.wait() 所有线程都在这里被锁住 print(arg) for i in range(10): t = threading.Thread(target=func, args=[i, ]) t.start() while True: value=input('>>') if value== '1': lock.set() 解锁
想释放多少进程就释放多少
import threading import time lock=threading.Condition() ##创建自由锁 def func(arg): time.sleep(2) lock.acquire() lock.wait() print(arg) lock.release() for i in range(10): t = threading.Thread(target=func, args=[i, ]) t.start() while True: value=input('>>') lock.acquire() lock.notify(int(value)) ##根据输入多少来释放相应的线程个数 lock.release()
连接池
预留固定的线程等待连接
from concurrent.futures import ThreadPoolExecutor import time import requests def tak(url): time.sleep(2) response=requests.get(url) print('返回结果',url,len(response.content)) pool=ThreadPoolExecutor(2) 建立一个线程池,预留两个线程 url_list=[ 'http://www.baidu.com', 'http://hao123.com', 'http://www.powercdn.com', ] for url in url_list: print('开始请求',url) pool.submit(tak,url) 调用池子
回调函数
from concurrent.futures import ThreadPoolExecutor import time import requests pool=ThreadPoolExecutor(3) def download(url): res=requests.get(url) return res 返回结果 def txt(future): download_res = future.result() print('处理中',download_res.url,download_res.status_code) url_list=[ 'http://www.baidu.com', 'http://hao123.com', 'http://www.powercdn.com', ] for url in url_list: print('开始请求',url) future=pool.submit(download,url) 拿到download处理返回结果 future.add_done_callback(txt) 调用txt函数处理download拿回来的数据
基于线程池的分布执行方式扩展
from concurrent.futures import ThreadPoolExecutor import requests def download(url): res=requests.get(url) return res def run(url_list): pool=ThreadPoolExecutor(2) for item in url_list: url=item['url'] call=item['call'] future=pool.submit(download,url) future.add_done_callback(call)
import 基于线程池的分布执行方式扩展 def f1(future): res=future.result() print(res.text) def f2(future): pass url_list=[ {'url':'http://www.baidu.com','call':f1}, {'url':'http://hao123.com','call':f2} ] 基于线程池的分布执行方式扩展.run(url_list)
Timer
定时器,规定时间以后执行某个操作
#_*_conding:utf-8_*_ __author__='wuzhihu' from threading import Timer def hello(): print('hello world') t=Timer(2,hello) ##设定为两秒以后执行hello函数 t.start()
python进程
创建
from multiprocessing import Process 导入模块 import threading import time def hello(i): print('say hi',i) if __name__=='__main__': window下执行进程必须这样做,不然会报错呦 for i in range(20): p=Process(target=hello,args=(i,)) p.start()
python协程
协程存在的意义:对于多线程应用,CPU通过切片的方式来切换线程间的执行,线程切换时需要耗时(保存状态,下次继续)。协程,则只使用一个线程,在一个线程中规定某个代码块执行顺序。
协程的适用场景:当程序中存在大量不需要CPU的操作时(IO),适用于协程;