一 线程
# 什么是进程 :是计算机资源分配的最小单位 # 什么是线程 # 线程和进程的关系 : # 每一个进程中都至少有一个线程 #开启100个线程
threading n=100 time.sleep(1 n-=1 (os.getpid(),% l= i range(100 t=Thread(target=func,args= i (n) n的结果是0
总结:
#每个进程里至少有一个主线程负责执行代码 # 在主线程中可以再开启一个新的线程 # 在同一个进程中就有两个线程同时在工作了 # 线程才是CPU调度的最小单位 # 多个线程之间的数据时共享的
# 主线程结束了之后守护线程也同时结束 # 守护线程会等待主线程完全结束之后才结束
from threading import Threadimport timedef foo():while True:print(123) time.sleep(1)def bar():print(456) time.sleep(3)print('end456') t1=Thread(target=foo) t2=Thread(target=bar) t1.daemon=True #foo是守护线程t1.start() t2.start()print("main----") #主线程结束的地方 打印结果:123 456main---- 123 123end456
三 锁(Lock)
1普通锁 lock
# 当你的程序中出现了取值计算再赋值的操作 数据不安全 —— 加锁
1 from threading import Thread,Lock 2 import time 3 def work(): 4 global n 5 6 lock.acquire() 7 temp=n 8 time.sleep(0.1) 9 n=temp-110 lock.release()11 if __name__ == '__main__':12 lock = Lock()13 n=10014 l=[]15 for i in range(10):16 p=Thread(target=work)17 l.append(p)18 p.start()19 for i in l:20 i.join()21 print(n)22 23 最后n的值为90
2 递归锁(Rlock)(很少用)
1 from threading import RLock2 3 lock = RLock()4 lock.acquire()5 lock.acquire()6 print(123)7 lock.release()8 print(456)9 lock.release()
总结:
# 普通的锁 在同一个线程中 只能acquire一次
# 所以当acquire两次的时候就容易出现死锁现象
# 出现了死锁现象可以使用递归锁去解决问题
# 但是本质上死锁的出现是因为逻辑的错误
# 因此我们更应该把注意力集中在解决逻辑错误
# 而不要在出现错误的时候直接用递归锁规避
递归锁并没有本质上解决死锁的问题
四 线程池(ThreadPoolExecutor)
1
1 import time 2 from concurrent.futures import ThreadPoolExecutor,ProcessPoolExecutor 3 def func(num): 4 print(num) 5 time.sleep(1) 6 print(num) 7 if __name__ == '__main__': 8 t=ThreadPoolExecutor(20) #20个线程,每次处理20个 9 for i in range(50):10 t.submit(func,i) #异步提交命令11 t.shutdown()#同join整个线程池12 print('done')
2 回调函数(callback)
1 # import time 2 # import random 3 # from concurrent.futures import ThreadPoolExecutor 4 # from threading import current_thread 5 # urls=[ 6 # 'https://www.baidu.com', 7 # 'https://www.python.org', 8 # 'https://www.openstack.org', 9 # 'https://help.github.com/',10 # 'http://www.sina.com.cn/'11 # 'http://www.cnblogs.com/'12 # 'http://www.sogou.com/'13 # 'http://www.sohu.com/'14 # ]15 #16 # def analies(content):17 # print('分析网页',current_thread())18 # print(content.result())19 #20 #21 # def get_url(url):22 # print('爬取网页',current_thread())23 # time.sleep(random.uniform(1,3))24 # # analies(url*10)25 # return url*1026 #27 # t = ThreadPoolExecutor(3)28 # print('主线程',current_thread())29 # for url in urls:30 # t.submit(get_url,url).add_done_callback(analies) #
#回调函数当执行了get_url之后,得到了一个url*10 ,然后在立即执行analies函数,并传给content参数 # concurrent.futures里面的 callback是由子线程做的
五协程(gevent)
需要手动安装,第三方模块
安装 :pip3 install gevent
协程 把一个线程拆分成几个 # 协程 是程序级别调度
# 减轻了操作系统的负担、增强了用户对程序的可控性
特点是:只要遇到阻塞就会执行别的任务。例如:
from gevent import monkey;monkey.patch_all()import geventimport timedef eat(name):print('%s eat 1' %name) time.sleep(2)print('%s eat 2' %name)def play(name):print('%s play 1' %name) time.sleep(1)print('%s play 2' %name) g1=gevent.spawn(eat,'egon') g2=gevent.spawn(play,'alex') g1.join() g2.join()# gevent.joinall([g1,g2])#第二种写法print('主')
执行结果是:
egon eat 1
alex play 1
alex play 2
egon eat 2
主
例2:利用协程批量访问url
1 from gevent import monkey;monkey.patch_all() #这个是必须加的否则gevent不识别time 2 import gevent 3 import requests 4 import time 5 6 def get_page(url): 7 print('GET: %s' %url) 8 response=requests.get(url) 9 if response.status_code == 200:10 print('%d bytes received from %s' %(len(response.text),url))11 12 13 start_time=time.time()14 gevent.joinall([15 gevent.spawn(get_page,'https://www.python.org/'),16 gevent.spawn(get_page,'https://www.yahoo.com/'),17 gevent.spawn(get_page,'https://github.com/'),18 ])19 stop_time=time.time()20 print('run time is %s' %(stop_time-start_time))