python-线程和进程
进程与线程:
1、什么是进程?
进程是程序的一次动态执行过程。每个进程都拥有自己的地址空间、内存、数据栈以及其他用于跟踪执行的辅助数据
操作系统负责其上所有进程的执行,操作系统会为这些进程合理地分配执行时间。
2、什么是线程?
线程是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。
一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。
一个线程是一个execution context(执行上下文),即一个cpu执行时所需要的一串指令。
假设你正在读一本书,没有读完,你想休息一下,但是你想在回来时恢复到当时读的具体进度。
有一个方法就是记下页数、行数与字数这三个数值,这些数值就是execution context。
如果你的室友在你休息的时候,使用相同的方法读这本书。
你和她只需要这三个数字记下来就可以在交替的时间共同阅读这本书了。
线程的工作方式与此类似。CPU会给你一个在同一时间能够做多个运算的幻觉,实际上它在每个运算上只花了极少的时间,
本质上CPU同一时刻只干了一件事。它能这样做就是因为它有每个运算的execution context。
就像你能够和你朋友共享同一本书一样,多任务也能共享同一块CPU。
3、什么是主线程?
主线程就是创建进程中产生的第一个线程,也就是main函数对应的线程。
4、线程ID
线程是一个轻量级进程,每一个用户态的线程 ,在内核中都对应这一个调度实体,也拥有着自己 的进程描述符。
tgid:线程组ID,线程组中的每一个线程的tgid都是相同的,在外表现为进程ID,它等于主线程的ID 。
tid:每一个线程在自己的用户层面上都有一个私有的pid,可以通过tid找到自己的虚拟地址,再通过页表映射到物理地址空间。在用户层面上展现的是线程的ID(tid),但在内核中它实际上是一个轻量级进程(pid)。
5、多线程
操作系统通过给不同的线程分配时间片(CPU运行时长)来调度线程,当CPU执行完一个线程的时间片后就会快速切换到下一个线程,
时间片很短而且切换切速度很快以至于用户根本察觉不到。早期的计算机是单核单线程的,
多个线程根据分配的时间片轮流被CPU执行,如今绝大多数计算机的CPU都是多核的,
多个线程在操作系统的调度下能够被多个CPU并发执行,程序的执行速度和CPU的利用效率大大提升。
绝大多数主流的编程语言都能很好地支持多线程,然而python由于GIL锁无法实现真正的多线程。
Process类:
1、创建进程:
1. p = Process(target=callable,name='',args='',kwargs='')
2. p.start() 启动进程
2、
主进程:执行的时候,默认的进程称作主进程
子进程:在主进程中可以开启子进程
p1 = Process(target=callable,args='')
os.getpid() 当前进程
os.getppid() 父进程
3、全局变量
如果是全局变量则,每个进程都会拥有一份全局变量。各自操作各自的全局变量
4、阻塞主进程
子进程.join()
阻塞主进程后面的代码
import time import os from multiprocessing import Process # print('----->top:', os.getpid()) def task1(): global number for i in range(5): print('洗衣服:', i, os.getpid(), os.getppid()) time.sleep(0.5) number -= 10 print('洗衣服:', number) def task2(n): global number for i in range(n): print('劳动最光荣,扫地中...', i, os.getpid(), os.getppid()) time.sleep(0.5) number -= 8 print('扫地:', number) number = 100 if __name__ == '__main__': print('main:--->start', os.getpid()) p1 = Process(target=task1) p2 = Process(target=task2, args=(6,)) p1.start() p2.start() # join() 加入 阻塞主进程后面的代码不执行 p1.join() p2.join() print('main:--->end', os.getpid()) print('main中的number是:', number)
5、进程对象可以访问的方法:
run()
start() 开始进程
join() 阻塞进程
terminate() 终止进程
close() 目标完成之后调用的close()释放资源
is_live 判断target任务是否完成,如果任务完成则False
import time import os from multiprocessing import Process def task1(): for i in range(5): print('洗衣服:',i,os.getpid(),os.getppid()) time.sleep(0.5) def taxk2(n): for i in range(n): print('劳动最光荣:...',i,os.getpid(),os.getppid()) time.sleep(0.5) if __name__=='__main__': p1 = Process(target=task1) p2 = Process(target=taxk2,args=(6,)) # p1.run() # p2.run() p1.start() p2.start() for i in range(10): if i==4: p1.terminate() # 终止p1进程 elif i==5: pass # p2.terminate() # p2.close() # 3.7中才能用 time.sleep(0.4) print('main:',i) print('p1:',p1.is_alive())
6、进程池:Pool
阻塞式和非阻塞式:
import os import time from multiprocessing import Pool def task1(): print('洗衣服:', os.getpid(), os.getppid()) time.sleep(0.5) return '我是进程:' + str(os.getpid()) # response = requests.get(url) # return response.content def callback(msg): print('{}洗衣服任务完成!'.format(msg)) # 保存下载的文件到本地 if __name__ == '__main__': pool = Pool(4) # 非阻塞式 for i in range(10): pool.apply_async(task1, callback=callback) # 添加任务结束 pool.close() # 阻塞主进程 pool.join() print('main over')
import os import time from multiprocessing import Pool def task1(): for i in range(5): print('洗衣服:',i, os.getpid(), os.getppid()) time.sleep(0.5) # return '我是进程:' + str(os.getpid()) if __name__ == '__main__': pool = Pool(4) # for i in range(10): pool.apply(task1) # 阻塞式: 进程池中一个任务完成之后才能做下一个任务 print('------------------------->',i) # 添加任务结束 pool.close() # 阻塞主进程 pool.join() print('main over')
7、进程间通信和信号量
队列:FIFO
put() : 队列中存放,如果满了则阻塞
get() : 从队列中取值,如果空了则阻塞
full() 判断是否是满了
empty() 判断是否是空了 如果空了,True
qsize() 获取长度
from multiprocessing import Process, Queue queue = Queue(3) queue.put('馒头1') queue.put('馒头2') queue.put('馒头3') print(queue.full()) # 判断是否满了 print(queue) print(queue.qsize()) try: queue.put('馒头4', timeout=3) queue.put('馒头5', timeout=3) except: print('存放馒头失败') while True: try: print(queue.get(timeout=1)) except: print('队列为空,取不出东西') break
例子:爬取图片
''' https://timgsa.baidu.com/timg?image&quality=80&size=b10000_10000&sec=1565782454&di=03886bb0af9fbd9d41896cea3fb184e2&src=http://img3.doubanio.com/view/photo/l/public/p2510470342.jpg https://ss1.bdstatic.com/70cFvXSh_Q1YnxGkpoWK1HF6hhy/it/u=3940930810,2726038075&fm=26&gp=0.jpg https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1565792399028&di=4a4ca85c5d2bae57fcde446658199a5c&imgtype=0&src=http%3A%2F%2Fn.sinaimg.cn%2Fent%2Fw880h495%2F20171213%2Ff41w-fypsqiz4586695.jpg https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1565792399024&di=4bedcbf80f19d5327ecff7ae6f1a9a13&imgtype=0&src=http%3A%2F%2Fimg.mp.itc.cn%2Fupload%2F20170619%2F3aa9d9dcc80141908b641e212ee9b846_th.jpg ''' import requests import time from multiprocessing import Process, Queue def download(url, queue): for image in url: response = requests.get(image) image_da = response.content queue.put(image_da) def save_file(queue): count = 0 while True: try: data = queue.get(timeout=5) filename = 'img' + str(count)+'.jpg' with open('images/'+filename, 'wb') as ws: ws.write(data) count+=1 print('保存{}完毕!'.format(filename)) except Exception as err: print('沒有更多数据了') break if __name__ == '__main__': q1 = Queue(6) images = [ 'https://timgsa.baidu.com/timg?image&quality=80&size=b10000_10000&sec=1565782454&di=03886bb0af9fbd9d41896cea3fb184e2&src=http://img3.doubanio.com/view/photo/l/public/p2510470342.jpg', 'https://ss1.bdstatic.com/70cFvXSh_Q1YnxGkpoWK1HF6hhy/it/u=3940930810,2726038075&fm=26&gp=0.jpg', 'https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1565792399028&di=4a4ca85c5d2bae57fcde446658199a5c&imgtype=0&src=http%3A%2F%2Fn.sinaimg.cn%2Fent%2Fw880h495%2F20171213%2Ff41w-fypsqiz4586695.jpg', 'https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1565792399024&di=4bedcbf80f19d5327ecff7ae6f1a9a13&imgtype=0&src=http%3A%2F%2Fimg.mp.itc.cn%2Fupload%2F20170619%2F3aa9d9dcc80141908b641e212ee9b846_th.jpg' ] p1 = Process(target=download, args=(images, q1)) p2 = Process(target=save_file, args=(q1,)) a = time.time() p1.start() p2.start() p1.join() p2.join() b = time.time() cha = b - a print(cha)
8、自定义进程
import time import os import requests from multiprocessing import Process, Queue class DownloadProgress(Process): def __init__(self,urls,queue): Process.__init__(self) self.urls = urls self.queue = queue # 重新父类的run方法 def run(self): for image in self.urls: filename = os.path.split(image)[1] response = requests.get(image) data = response.content self.queue.put(data) self.queue.get() print('下载{}完毕!!'.format(filename)) self.queue.close() if __name__ == '__main__': q1 = Queue(4) images = [ 'https://ss3.bdstatic.com/70cFv8Sh_Q1YnxGkpoWK1HF6hhy/it/u=4151794834,2429338731&fm=26&gp=0.jpg', 'https://ss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=3247242571,2598044801&fm=26&gp=0.jpg', 'https://ss1.bdstatic.com/70cFvXSh_Q1YnxGkpoWK1HF6hhy/it/u=3639290340,1034325833&fm=26&gp=0.jpg', 'https://ss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=1853363442,2878778500&fm=26&gp=0.jpg' ] dlprocess = DownloadProgress(images,q1) dlprocess.start() dlprocess.join() print('main')
线程
python 如何创建一个线程?
线程:
1. t1 = Thread(target=task1)
2. 自定义线程
run()
start()
join()
name: 默认的Thread-1, Thread-2,....
current_thread().name 获取当前线程的名字
is_alive
import os import time from threading import Thread,current_thread def task1(): for i in range(5): print('{}洗衣服:'.format(current_thread().name), i, os.getpid(), os.getppid()) time.sleep(0.5) def task2(n): for i in range(n): print('{}劳动最光荣,扫地中...'.format(current_thread().name), i, os.getpid(), os.getppid()) time.sleep(0.5) if __name__ == '__main__': print('main:', os.getpid()) # 创建线程对象 t1 = Thread(target=task1,name='警察') t2 = Thread(target=task2,name='小偷', args=(6,)) # 启动线程 t1.start() t2.start() # t1.join() # t2.join() for i in range(3): print("t1:", t1.is_alive()) print("t2:", t2.is_alive()) print('main:', i) time.sleep(1)
自定义线程:
1. 定义一个类继承Thread
2. 重写: [__init__] 必须重写: run()
3. 创建线程类对象
4. 启动线程
import time from threading import Thread class MyThread(Thread): def __init__(self, name): Thread.__init__(self) self.name = name def run(self): for i in range(5): print('{}正在打印:{}'.format(self.name, i)) time.sleep(0.1) if __name__ == '__main__': t1 = MyThread('小明') t2 = MyThread('小花') t3 = MyThread('ergou') t1.start() t2.start() t3.start()
共享数据:
如果有全局变量,则每个线程是共享的
GIL:共享锁
import time from threading import Thread,current_thread # 全局变量 ticket = 10 def sale_ticket(): global ticket while True: if ticket>0: print('{}正在卖{}张火车票!!'.format(current_thread().name,ticket)) ticket-=1 time.sleep(1) else: break if __name__ == '__main__': t1 = Thread(target=sale_ticket,name='1号窗口') t2 = Thread(target=sale_ticket,name='2号窗口') t3 = Thread(target=sale_ticket,name='3号窗口') t1.start() t2.start() t3.start()
from threading import Thread, Lock number = 0 def task(lock): global number lock.acquire() # 握住 for i in range(100000): number += 1 lock.release() # 释放锁 if __name__ == '__main__': lock = Lock() t1 = Thread(target=task, name='1号窗口', args=(lock,)) t2 = Thread(target=task, name='2号窗口', args=(lock,)) t3 = Thread(target=task, name='3号窗口', args=(lock,)) t1.start() t2.start() t3.start() t1.join() t2.join() t3.join() print('number:', number)