定义:python
进程: 是对各类资源管理的集合,qq 要以一个总体的形式暴露给操做系统管理,里面包含对各类资源的调用,内存的管理,网络接口的调用等算法
线程: 是操做系统最小的调度单位, 是一串指令的集合。编程
进程要想操做CPU,就必需要建立一个线程(进程中至少包含一个线程)网络
区别:多线程
1.线程共享内存空间(共享数据等),进程的内存空间是独立的并发
2.同一进程的线程之间能够相互交流 ,2个进程之间的交流必须经过一个中间代理app
3.线程能够操做和控制其余线程(同一进程下),进程只能操做和控制子进程。框架
对主线程的更改可能会影响到其余线程的工做,对父进程的更改(除非关闭)不会影响子进程。(子进程还能够派生子进程)异步
import threading def run(n): print('运行线程',n) for i in range(10): # 建立10个线程 t = threading.Thread(target=run, args=(i,)) # 线程运行的函数和参数 t.setDaemon(True) # 设置为守护线程(在主线程线程结束后自动退出,默认为False即主线程线程结束后子线程仍在执行) t.start() # 启动线程
上述代码建立了10个“前台”线程,而后控制器就交给了CPU,CPU根据指定算法进行调度,分片执行指令。scrapy
更多方法:
因为线程之间是进行随机调度,而且每一个线程可能只执行n条执行以后,当多个线程同时修改同一条数据时可能会出现脏数据,因此,出现了线程锁 - 同一时刻容许一个线程执行操做。
import threading import time gl_num = 0 lock = threading.RLock() # 定义线程锁 def Func(): lock.acquire() # 开始锁 global gl_num gl_num += 1 time.sleep(1) print(gl_num) lock.release() # 结束锁 for i in range(10): t = threading.Thread(target=Func) t.start() """ 没有锁的时候打印: >> 10,10,10,10,10,10,10,10,10,10 有锁的时候打印: >> 1,2,3,4,5,6,7,8,9,10 """
互斥锁同时只容许一个线程更改数据,而信号量锁是同时容许必定数量的线程更改数据 ,多个线程同时执行完毕。
import threading, time def run(n): semaphore.acquire() # 信号量锁开始 time.sleep(1) print("当前运行线程为: %s" % n) semaphore.release() # 结束 if __name__ == '__main__': num = 0 semaphore = threading.BoundedSemaphore(5) # 最多容许5个线程同时运行(5个5个一块儿出来) for i in range(20): t = threading.Thread(target=run, args=(i,)) t.start()
python线程的事件用于主线程控制其余线程的执行,事件主要提供了两个方法:event.set()设定,event.clear()没设定。
1 import time,threading 2 3 event = threading.Event() 4 5 def lighter(): 6 count=0 7 event.set() #设定 8 while True: 9 if count<10 and count>=5: 10 event.clear() #清除设定 11 print("\033[41;1m红灯\033[0m") 12 time.sleep(1) 13 elif count>10: 14 count=0 15 event.set() 16 else: 17 print("\033[42;1m绿灯\033[0m") 18 time.sleep(1) 19 count+=1 20 21 def car(name): 22 while True: 23 if event.is_set(): #判断是否设定 24 print("\033[32;1m[%s] run...\033[0m"%name) 25 time.sleep(1) 26 else: 27 print('\033[31;1m [%s] stop...'%name) 28 event.wait() #等待设定 29 print('\033[33;1m [%s]走咯'%name) 30 31 32 light=threading.Thread(target=lighter,) 33 34 light.start() 35 36 car1=threading.Thread(target=car,args=('Tesla',)) 37 38 car1.start()
多进程特色:
from multiprocessing import Process def foo(i): print('say hi', i) if __name__ == '__main__': for i in range(10): p = Process(target=foo, args=(i,)) p.start()
获取进程id:
1 from multiprocessing import Process #多进程 2 import os 3 4 def info(title): 5 print(title) 6 print('module name:', __name__) 7 print('parent process:', os.getppid()) #获取父进程的端口号 8 print('process id:', os.getpid()) #获取当前进程的端口号 9 print('\n') 10 11 def f(name): 12 info('\033[31;1mcalled from child process function f\033[0m') 13 print('hello', name) 14 15 if __name__ == '__main__': 16 info('\033[32;1mmain process line\033[0m') 17 p = Process(target=f, args=('bob',)) 18 p.start() 19 p.join() 20 21 get进程id
经过队列共享数据:
from multiprocessing import Process, Queue def run(qq): qq.put("123") if __name__=='__main__': q=Queue() #生成一个队列,经过队列进行传递数据 p=Process(target=run,args=(q,)) p.start() print(q.get()) p.join()
经过字典共享数据:
from multiprocessing import Process, Manager import os def f(d, l): d[os.getpid()] =os.getpid() l.append(os.getpid()) print(l) if __name__ == '__main__': with Manager() as manager: #Manager()=manager d = manager.dict() #{} #生成一个字典,可在多个进程间共享和传递 l = manager.list(range(5))#生成一个列表,可在多个进程间共享和传递 p_list = [] for i in range(10): p = Process(target=f, args=(d, l)) p.start() p_list.append(p) for res in p_list: #等待结果 res.join() print(d) print(l)
在多个进程共享一个屏幕时可能会致使输出的数据变乱。
from multiprocessing import Process, Lock def f(l, i): l.acquire() #设置进程锁 try: print('hello world', i) finally: l.release() #取消进程锁 if __name__ == '__main__': l = Lock() #实例化锁 for num in range(10): Process(target=f, args=(l, num)).start()
进程池内部维护一个进程序列,当使用时,则去进程池中获取一个进程,若是进程池序列中没有可供使用的进进程,那么程序就会等待,直到进程池中有可用进程为止。
进程池中有两个方法:
1 from multiprocessing import Pool 2 import time 3 import os 4 5 def Foo(i): 6 time.sleep(2) 7 print("in process",os.getpid()) 8 return i + 100 9 10 def Bar(arg): 11 print('-->exec done:', arg,os.getpid()) 12 13 if __name__ == '__main__': 14 #freeze_support() 15 pool = Pool(processes=3) #容许进程池同时放入5个进程 16 print("主进程",os.getpid()) 17 for i in range(10): 18 pool.apply_async(func=Foo, args=(i,), callback=Bar) #callback=回调 19 #pool.apply(func=Foo, args=(i,)) #串行 20 #pool.apply_async(func=Foo, args=(i,)) #并行 21 print('end') 22 pool.close() 23 pool.join() #进程池中进程执行完毕后再关闭,若是注释,那么程序直接关闭。.join()
经过单线程实现并发(协程只有一个线程,so不用锁)
协程的好处:
缺点:
使用yield实现协程操做例子
1 import time 2 3 def consumer(name): 4 print("--->starting eating baozi...") 5 while True: 6 new_baozi = yield 7 print("[%s] is eating baozi %s" % (name, new_baozi)) 8 # time.sleep(1) 9 10 def producer(): 11 r = con.__next__() 12 r = con2.__next__() 13 n = 0 14 while n < 5: 15 n += 1 16 con.send(n) 17 con2.send(n) 18 time.sleep(1) 19 print("\033[32;1m[producer]\033[0m is making baozi %s" % n) 20 21 if __name__ == '__main__': 22 con = consumer("c1") 23 con2 = consumer("c2") 24 p = producer() 25 26 yield
同步与异步性能差异
1 import gevent 2 3 def task(pid): 4 gevent.sleep(0.5) 5 print('Task %s done' % pid) 6 7 def synchronous(): #每一个都要等0.5s,须要5s 8 for i in range(1, 10): 9 task(i) 10 11 def asynchronous(): #一共等0.5s 12 threads = [gevent.spawn(task, i) for i in range(10)] 13 gevent.joinall(threads) 14 15 print('Synchronous:') 16 synchronous() 17 18 print('Asynchronous:') 19 asynchronous() 20 21 同步与异步
操做系统方面的多进程,多线程,协程:
操做系统能够开多个进程,一个进程能够有多个线程,多个线程能够被分配到不一样的核心上跑,但实际上每一个核心上只有一个线程,只是这个线程在不停的进行上下文的切换,给咱们一种并发的感受。
协程:单线程的调度机制。它的做用是让原来要使用异步+回调方式(调用线程)写的非人类代码,能够用看似同步的方式写出来。协程是先出现的,但它有明显的时间差,没有并发的感受,因此出现了线程。
python的多进程,多线程,协程:
但python的多线程只能在一个核心上跑(创始人没想到会有多核出现),就是单核的上下文切换,因此很鸡肋。因而协程在python大展拳脚,好多框架都是使用协程来解决多任务的,而不是线程(scrapy,tornado)。
python中多进程,多线程,协程的使用:
IO密集型:多线程/协程(能够用异步),cpu占用率低,单个cpu核心就够了
CPU密集型:多进程,多给它几个核心提高性能
1 python 默认参数建立线程后,无论主线程是否执行完毕,都会等待子线程执行完毕才一块儿退出,有无join结果同样
2 若是建立线程,而且设置了daemon为true,即thread.setDaemon(True), 则主线程执行完毕后自动退出,不会等待子线程的执行结果。并且随着主线程退出,子线程也消亡。
3 join方法的做用是阻塞,等待子线程结束,join方法有一个参数是timeout,即若是主线程等待timeout,子线程尚未结束,则主线程强制结束子线程。
4 若是线程daemon属性为False, 则join里的timeout参数无效。主线程会一直等待子线程结束。
5 若是线程daemon属性为True, 则join里的timeout参数是有效的, 主线程会等待timeout时间后,结束子线程。此处有一个坑,即若是同时有N个子线程join(timeout),那么实际上主线程会等待的超时时间最长为 N * timeout, 由于每一个子线程的超时开始时刻是上一个子线程超时结束的时刻。