进程线程
进程就是一个任务,拥有自己独立的内存空间,用于一个以上的线程。
线程:操作系统能够进行运算调度的最小单位。可以理解为轻量级的进程,每个进程启动后,会默认产生一个主线程,主线程可以创建多个子线程,一个进程内的线程可以共享部分资源。
一个任务就是一个进程,一个进程中至少有一个线程。
多线程和多进程执行方式是一样的,都是操作系统在多个线程之间快速切换,交替运行。
当然真正同时执行多线程想要多核CPU才能实现。
多任务的实现三种方法:
多进程模式
多线程模式
多进程+多线程模式
多进程:数据共享复杂,占用内存多,切换复杂,CPU利用率低。进程间互不影响。
多线程:数据共享,占用内存少,切换简单,CPU利用率高。一个线程挂掉将导致整个进程挂掉。
多进程
multiprocessing模块提供一个Process类来代表进程对象
from multiprocessing import Process
def func(name):
print(name)
if __name__ == '__main__':
p=Process(target=func,args=('test',)) //创建Process实例
p.start() //启动
p.join() //等待子进程结束后再继续往下运行,通常用于进程间的同步
Poll
使用进程池创建大量子进程
from multiprocessing import Poll
def func(name):
print(name)
if __name__ == '__main__':
p=Pool(4)
for i in range(5):
p.apply_async(func,args=("test",))
p.close() //不在继续添加新的Process
p.join()
进程间通信
进程间通信是通过Queue、Pipes(管道)等实现的
from multiprocessing import Process, Queue
import os, time, random
# 写数据进程执行的代码:
def write(q):
print('Process to write: %s' % os.getpid())
for value in ['A', 'B', 'C']:
print('Put %s to queue...' % value)
q.put(value)
time.sleep(random.random())
# 读数据进程执行的代码:
def read(q):
print('Process to read: %s' % os.getpid())
while True:
value = q.get(True)
print('Get %s from queue.' % value)
if __name__=='__main__':
# 父进程创建Queue,并传给各个子进程:
q = Queue()
pw = Process(target=write, args=(q,))
pr = Process(target=read, args=(q,))
# 启动子进程pw,写入:
pw.start()
# 启动子进程pr,读取:
pr.start()
# 等待pw结束:
pw.join()
# pr进程里是死循环,无法等待其结束,只能强行终止:
pr.terminate()
多线程(多个线程并发的技术)
threading模块
启动一个线程就是把一个函数传入并创建Thread实例,然后调用start()开始执行。
import threading
def func():
print(threading,current_thread().name)
t=threading.Thread(target=func,name="Func_name")
t.start()
t.join()
任何进程都会启动一个线程,为主线程,主线程又可以启动新的线程。
Lock
多线程与多进程最大的不同在于
多进程:同一个变量,各自有一份拷贝存在每个进程中,互不影响
多线程:所有变量都由所有线程共享,任何变量都可被任何线程修改,因此线程之间共享数据最大的危险在于多个线程同时修改一个变量。
为了确保数据安全,就需要使用线程锁,当一个线程执行时,该线程因为获得了锁,其他线程不能同时执行,直到锁被释放。
import threading
balance =0 //共享的数据
lock=threading.Lock()
def change_it(n):
global balance
balance = balance + n
balance = balance - n
def func(n):
for i in range(100):
lock.acquire()
try:
change_it(n)
finally:
lock.release()
t1=threading.Thread(target=func,args=(5,))
t2=threading.Thread(target=func,args=(5,))
t1.start()
t2.start()
t1.join()
t2.join()
进程 vs. 线程
多进程优点:稳定性高,一个进程奔溃了,不影响主进程和其他子进程,当然主进程挂了,所有进程就全挂了。
缺点:代价大,操作系统能同时运行的进程数也是有限的。
多线程优点:比多进程快一点,但是也快不到哪去
缺点:任何一个线程挂点都可能造成整个进程的奔溃,因为所有线程共享进程的内存。
异步IO
CPU的速度远远快于磁盘、网络。在一个线程中,CPU执行代码速度极快,一旦遇到IO操作,如读写文件,发送网络数据时,就需要等待IO操作完成,才能继续执行下一步操作。这种情况称为异步IO。
在IO操作中,如果当前线程被挂起,而其他需要CPU执行的代码就无法被当前线程执行。
因为一个IO操作就阻塞了当前线程,导致其他代码无法执行,所以我们使用多进程多线程来并发执行代码。
多进程(线程)虽然解决了并发问题,但系统不能无上限增加线程。
当代码需要执行一个耗时操作时,他只发出IO指令,并不等待IO结果,然后就去执行其他代码了。一段时间后,当IO返回结果时,再通知CPU进行处理。
线程池
一个线程的运行时间可以分为3个部分:线程的启动时间、线程的运行时间和线程的销毁时间。如果线程不能被重复利用,必增加系统的相应时间,降低了效率。
使用线程池:
线程预先被创建然后放入到线程池中,同时处理完当前任务之后并不会被销毁而是被安排处理下一个任务,避免多次创建线程,带来更好的性能和系统的稳定。
为什么使用线程池与优势
1.减少了创建和销毁线程的次数,每个工作线程都可能被重复利用,可执行多个任务。
2.可以根据系统的承受能力,调整线程池中工作线程的数目,防止因为消耗过多的内存。
GIL
全局解释器锁,同一进程中假如有多个线程运行,一个线程在运行python程序时会霸占python解释器(即加了一把锁),使该进程内的其他线程无法运行,等该线程运行完成后其他线程才能运行。如果线程运行过程中遇到耗时操作,则解释器锁打开,其他线程运行。所以在多线程中,线程的运行仍是有先后顺序的,并不是同时运行。
装饰器
他可以让函数在不做任何变动的情况下增加额外的功能,返回值也是一个函数对象,插入日志,性能测试,事务处理,缓存和权限验证。
多线程
python并不支持真正意义上的多线程,python提供了多线程包,python中有一个GIL ,它确保你的代码永远只有一个线程在执行。经过GIL处理,会增加执行的开销,如果你的代码是IO密集型,多线程可以明显提高效率,相反你的代码是CPU密集型的多线程大部分是鸡肋。
os模块文件的操作
os.remove()
os.rename()
os.mkdir()
os.chmod()
操作系统的设计
1.以多进程形式,允许多个任务同时进行
2.以多线程形式,允许单个任务分成不同的部分运行
3.提供协调机制,一方面防止进程间和线程之间的冲突,另一方面允许进程之间和线程之间资源的共享
什么时候用多线程 什么时候用多进程
1.频繁创建销毁,需要进行大量计算的,多核分布的
2.多机分布
线程安全与不安全
加锁的就是安全的,不加锁的就是不安全的