9-3 线程,线程池,守护线程,锁,协程

一 线程

# 什么是进程 :是计算机资源分配的最小单位
# 什么是线程
# 线程和进程的关系 :
    # 每一个进程中都至少有一个线程
#开启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))

 

 

上一篇:【Python从零到壹】Python列表详解


下一篇:CSP-S 2019 题解(部分)& 游记(伪)