107、进程与线程
进程、线程、与协成
1.1 进程介绍
程序并不能单独运行,只有将程序装载到内存中,系统为它分配资源才能运行,而这种执行的程序就称之为进程。
程序和进程的区别就在于:程序是指令的集合,它是进程运行的静态描述文本;进程是程序的一次执行活动,属于动态概念。
在多道编程中,我们允许多个程序同时加载到内存中,在操作系统的调度下,可以实现并发地执行。这是这样的设计,大大提高了CPU的利用率。进程的出现让每个用户感觉到自己独享CPU,因此,进程就是为了在CPU上实现多道编程而提出的。
进程的缺点:
-
进程只能在一个时间干一件事,如果想同时干两件事或多件事,进程就无能为力了。
-
进程在执行的过程中如果阻塞,例如等待输入,整个进程就会挂起,即使进程中有些工作不依赖于输入的数据,也将无法执行。
1.2 线程介绍
线程是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务
1.3 线程和进程的区别
- 进程的内存独立,线程共享同一进程的内存
- 进程是资源的集合,线程是执行单位
- 进程之间不能之间互相访问,线程可以相互通信
- 创建新进程非常消耗资源,线程非常轻量,只保存线程需要运行时的必要数据,如上下文
- 同一进程里的线程可以相互控制,父线程可以控制子线程
- io 密集型应用使用多线程,
- 计算密集型应用使用多进程
1.4 GIL 锁
python 中某一进程中的多个线程只能被一个 CPU 调用,因为在线程中存在 GIL(全局解释器锁)。而C语言一个进程的多线程可以分别被不同的 CPU 调用
2、threading模块
2.1 线程的继承
import threading
import time
class MyThread(threading.Thread):
def __init__(self,num):
threading.Thread.__init__(self)
self.num = num
def run(self):#定义每个线程要运行的函数
print("running on number:%s" %self.num)
time.sleep(3)
if __name__ == '__main__':
t1 = MyThread(1)
t2 = MyThread(2)
t1.start()
t2.start()
2.2 创建多线程与join
import threading,time
def hellow(msg):
print('this is hellow %s' %msg)
time.sleep(1)
if __name__ == '__main__':
thread_list = []
for i in range(10):
t1 = threading.Thread(target=hellow,args=(i,))#生成一个线程
t1.start()
thread_list.append(t1)
print(t1.getName())#获取线程名
for r in thread_list:
r.join()#等待该线程执行完毕再往下执行,join(2)就是等待的最长时间
print('-------主线程-------')
2.3 守护线程Daemon
import threading,time
'''守护线程会在主线程执行完毕时立刻断开'''
def sayhi(msg):
time.sleep(0.001)
print('this is msg',msg)
if __name__ == '__main__':
for i in range(10):
t1 = threading.Thread(target=sayhi,args=(i,))
t1.setDaemon(True)#将 t1 设为主线程的守护线程
t1.start()
print('------主线程------')
每次输出的结果会有不同,有的线程没执行完就跟着主线程down了
2.4 小结
- start 线程准备就绪,等待CPU调度
- setName 为线程设置名称
- getName 获取线程名称
- setDaemon 设置为后台线程或前台线程(默认),如果是后台线程,主线程执行过程中,后台线程也在进行,主线程执行完毕后,后台线程不论成功与否,均停止;如果是前台线程,主线程执行过程中,前台线程也在进行,主线程执行完毕后,等待前台线程也执行完成后,程序停止
- join 逐个执行每个线程,执行完毕后继续往下执行,该方法使得多线程变得无意义
- run 线程被cpu调度后自动执行线程对象的run方法
2.5 线程锁
单线程锁 Lock、RLock
import threading,time
num = 10
#lock = threading.Lock()#创建线程锁,一次只能锁一个,只能一次申请锁
lock = threading.RLock()#创建递归锁,一次只能锁一个,多次次申请锁
#lock = threading.BoundedSemaphore(3)#创建信号量锁,一次锁定3人
def task(arg):
lock.acquire()#申请使用锁
lock.acquire()#RLock多次申请使用锁,递归锁
time.sleep(1)
global num
num -= 1
print(num)
lock.release()#释放锁
lock.release()#RLock多次释放锁
for i in range(10):
t = threading.Thread(target=task,args=[i,])
t.start()
print('-----主线程-----',num)
多线程 Semaphore
lock = threading.BoundedSemaphore(3)#三线程锁
def task(arg):
# 申请使用锁,其他人等
lock.acquire()
time.sleep(1)
global v
v -= 1
print(v)
# 释放
lock.release()
for i in range(10):
t = threading.Thread(target=task,args=(i,))
t.start()
事件 event
python线程的事件用于主线程控制其他线程的执行,事件主要提供了三个方法 set、wait、clear。
事件处理的机制:全局定义了一个“Flag”,如果“Flag”值为 False,那么当程序执行 event.wait 方法时就会阻塞,如果“Flag”值为True,那么event.wait 方法时便不再阻塞。
- clear:将“Flag”设置为False
- set:将“Flag”设置为True
lock = threading.Event()
def task(arg):
time.sleep(1)
# 锁住所有的线程
lock.wait()
# 申请使用锁,其他人等
print(arg)
for i in range(10):
t = threading.Thread(target=task,args=(i,))
t.start()
while True:
value = input('>>>>')
if value == '1':
lock.set()
lock.clear()
条件 Condition
使得线程等待,只有满足某条件时,才释放n个线程
lock = threading.Condition()
def task(arg):
time.sleep(1)
# 锁住所有的线程
lock.acquire()
lock.wait()
# 申请使用锁,其他人等
print('线程',arg)
lock.release()
for i in range(10):
t = threading.Thread(target=task,args=(i,))
t.start()
while True:
value = input('>>>>')
lock.acquire()
lock.notify(int(value))
lock.release()
定时器 Timer
定时器,指定n秒后执行某操作
from threading import Timer
def hello():
print("hello, world")
t = Timer(1, hello)
t.start() # after 1 seconds, "hello, world" will be printed
2.5 线程池
from concurrent.futures import ThreadPoolExecutor
import time,requests
pool = ThreadPoolExecutor(2)#定义线程池每次最多运行两个线程
url_list = [
'http://www.baidu.com',
'http://www.oldboyedu.com',
'http://www.qq.com',
]
def download(url):
response = requests.get(url)
#print(response)
return response
def option(future):
download_response = future.result()
print('option',download_response.url,download_response.status_code,download_response.text)
for url in url_list:
print('开始请求',url)
future = pool.submit(download,url)#去线程池获取连接
future.add_done_callback(option)#调用option函数,并将download的结果传参
3、multiprocessing 模块
进程各自持有一份数据,默认无法共享数据
from multiprocessing import Process
import time
def option(num,li):
li.append = num
print(li)
if __name__ == '__main__':
v = []
# for i in range(5):
# p = threading.Thread(target=option,args=(i,v,))
# p.start()
for i in range(5):
p1 = Process(target=option,args=(i,v,))
p1.start()
3.1 进程间数据共享
方法一:Array
from multiprocessing import Process,Array,Manager
import threading
def option(num,li):
li[num] = 1
print(list(li))
if __name__ == '__main__':
v = Array('i',5)#('数据类型',长度)
for i in range(5):
p = Process(target=option,args=(i,v,))
p.start()
方法二:manage.dict()共享数据
from multiprocessing import Process,Array,Manager
import threading
def option(num,li):
li.append(num)
print('打印信息',li)
if __name__ == '__main__':
v = Manager().list()
for i in range(10):
p = Process(target=option,args=(i,v,))
p.start()
p.join()
3.2 进程池
进程锁与线程锁一样
进程池内部维护一个进程序列,当使用时,则去进程池中获取一个进程,如果进程池序列中没有可供使用的进进程,那么程序就会等待,直到进程池中有可用进程为止。
from concurrent.futures import ProcessPoolExecutor
from multiprocessing import Process,Manager
def call(arg):
data = arg.result()
print(data)
def option(arg):
print('打印信息',arg)
return arg + 100
if __name__ == '__main__':
pool = ProcessPoolExecutor(5)
for i in range(10):
obj = pool.submit(option,i)
obj.add_done_callback(call)
4、协成
协程存在的意义:对于多线程应用,CPU通过切片的方式来切换线程间的执行,线程切换时需要耗时(保存状态,下次继续)。协程,则只使用一个线程,在一个线程中规定某个代码块执行顺序。
协程的适用场景:当程序中存在大量不需要CPU的操作时(IO),适用于协程;
from greenlet import greenlet
def test1():
print(12)
gr2.switch()
print(34)
gr2.switch()
def test2():
print(56)
gr1.switch()
print(78)
gr1 = greenlet(test1)
gr2 = greenlet(test2)
gr1.switch()
根据协程二次开发:协程+IO
from gevent import monkey; monkey.patch_all()
import gevent
import requests
def f(url):
response = requests.get(url)
print(response.url,response.status_code)
gevent.joinall([
gevent.spawn(f, 'http://www.oldboyedu.com/'),
gevent.spawn(f, 'http://www.baidu.com/'),
gevent.spawn(f, 'http://github.com/'),
])