IO 操作 :又叫文件操作,文件操作分为从文件中读取内容和往文件中写入内容。
CPU操作:计算,运算
1.多线程(IO密集)
特点:
#线程的并发是利用cpu上下文的切换(是并发,不是并行)同一时间只有一个cpu在运行
#多线程执行的顺序是无序的
#多线程共享全局变量,但是不能修改,只能查看
#线程是继承在进程里的,没有进程就没有线程
#GIL全局解释器锁
#只要在进行耗时的IO操作的时候,能释放GIL,所以只要在IO密集型的代码里,用多线程就很合适
实例1:
import threading
import time
def test1(n):
time.sleep(1)
print('task', n)
t1=threading.Thread(target=test1,args=('t%s'%2,)) # t1线程
t1.start()
启动多个线程:
for i in range(10):
t = threading.Thread(target=test1,args=('t-%s' % i,))
t.start()
task t-0
task t-1
task t-2
task t-5
task t-4
task t-3
task t-6
task t-8
task t-9
task t-7
从结果可以看出时无序的
实例2:
num=0
def test1():
global num #如果不用globa num ,就不能更改全局变量,只能查看
num+=100
print(num)
test1()
实例3:
num=0
def test1():
global num
num+=100
# print(num)
# test1()
def test2():
print(num)
t1=threading.Thread(target=test1)
t2=threading.Thread(target=test2)
t1.start()
t2.start()
输出结果如下:
100
从结果可以看出多线程中全局变量是共享的
GIL的全称是:Global Interpreter Lock,意思就是全局解释器锁,同一进程中假如有多个线程运行,一个线程在运行python程序的时候会霸占python解释器(加了一把锁即GIL),使该进程内的其他线程无法运行,等该线程运行完后其他线程才能运行。如果线程运行过程中遇到耗时操作,则解释器锁解开,使其他线程运行。所以在多线程中,线程的运行仍是有先后顺序的,并不是同时进行。
多进程中因为每个进程都能被系统分配资源,相当于每个进程有了一个python解释器,所以多进程可以实现多个线程的同时运行,缺点是进程系统资源开销大。
这个GIL并不是python的特性,他是只在Cpython解释器里引入的一个概念,而在其他的语言编写的解释器里就没有这个GIL例如:Jython,Pypy
为什么会有gil?:
随着电脑多核cpu的出现核cpu频率的提升,为了充分利用多核处理器,进行多线程的编程方式更为普及,随之而来的困难是线程之间数据的一致性和状态同步,而python也利用了多核,所以也逃不开这个困难,为了解决这个数据不能同步的问题,设计了gil全局解释器锁。
说到gil解释器锁,我们容易想到在多线程中共享全局变量的时候会有线程对全局变量进行的资源竞争,会对全局变量的修改产生不是我们想要的结果,而那个时候我们用到的是python中线程模块里面的互斥锁,哪样的话每次对全局变量进行操作的时候,只有一个线程能够拿到这个全局变量;看下面的代码:
import time
global_num = 0
def test1():
global global_num
for i in range(1000000):
global_num += 1
def test2():
global global_num
for i in range(1000000):
global_num += 1
t1 = threading.Thread(target=test1)
t2 = threading.Thread(target=test2)
t1.start()
t2.start()
t1.join()
t2.join() # join等待线程执行完毕
print(global_num)
1209878 #结果比实际结果(2000000)要小
出现了重复赋值的操作,所以数值少,数值越多,发生重复的概率越多出错的概率越大,因此数结果比实际小的概率大。
为了让结果正确,不出现重复赋值的操作,加入互斥锁来实现,加上互斥锁后,会在一个函数运行完后再去运行下一个函数,因此此时是串行,无法避免。
import time
lock = threading.Lock()
global_num = 0
def test1():
global global_num
lock.acquire()
for i in range(1000000):
global_num += 1
lock.release()
def test2():
global global_num
lock.acquire()
for i in range(1000000):
global_num += 1
lock.release()
t1 = threading.Thread(target=test1)
t2 = threading.Thread(target=test2)
t1.start()
t2.start()
t1.join()
t2.join()
print(global_num)
2000000 #此时输出结果正确
2. 多进程(CPU密集)
#一个程序运行起来之后,代码+用到的资源称之为进程,它是操作系统分配资源的基本单位,不仅可以通过线程完成多任务,进程也是可以的
#进程之间是相互独立的
#cpu密集的时候适合用多进程
#多进程比较耗费资源
2.1多进程并发
import time
import multiprocessing #多进程模块
def test1():
for i in range(10):
time.sleep(1)
print('task1',i)
def test2():
for i in range(10):
time.sleep(1)
print('task2',i)
if __name__ == '__main__': # 必须有这个,否则报错
p1=multiprocessing.Process(target=test1)
p2=multiprocessing.Process(target=test2)
p1.start()
p2.start()
运行结果如下:
task1 0
task2 0
task1 1
task2 1
task1 2
task2 2
task1 3
task2 3
task1 4
task2 4
task1 5
task2 5
task1 6
task2 6
task1 7
task2 7
task1 8
task2 8
task1 9
task2 9
可以看出同一时间运行两个任务
2.2进程之间的数据不共享
import multiprocessing
n=0
def test1():
global n
for i in range(10):
n+=1
def test2():
print(n)
if __name__ == '__main__':
p1=multiprocessing.Process(target=test1)
p2=multiprocessing.Process(target=test2)
p1.start()
p2.start()
结果如下:
0
由此可以看出进程之间是不共享的
2.3进程池并发
import time
import multiprocessing
from multiprocessing import Pool
def test1():
for i in range(10):
time.sleep(1)
print('task1',i)
def test2():
for i in range(10):
time.sleep(1)
print('task2',i)
if __name__ == '__main__':
pool=Pool(2) # ()里的数字代表同时运行的进程数
pool.apply_async(test1)
pool.apply_async(test2)
pool.close() # 必须这么写,规定
pool.join()
运行结果如下:
task1 0
task2 0
task1 1
task2 1
task1 2
task2 2
task1 3
task2 3
task1 4
task2 4
task1 5
task2 5
task1 6
task2 6
task1 7
task2 7
task1 8
task2 8
task1 9
task2 9
3.协程并发(gevent)
协程也叫微线程,它是在一个线程里面的,协程遇见IO就切换
进程是资源分配的单位
线程是操作系统调度的单位
进程切换需要的资源最大,效率低
线程切换需要的资源一般,效率一般
协程切换任务资源很小,效率高
多进程、多线程根据cpu核数不一样可能是并行的,但是协成在一个线程中
实例1:
import time
import gevent
def test1():
for i in range(10):
gevent.sleep(1) # gevent不支持time,因此需要用gevent.sleep()
# time.sleep(1)
print('task1',i)
def test2():
for i in range(10):
gevent.sleep(1)
# time.sleep(1)
print('task2',i)
g1=gevent.spawn(test1)
g2=gevent.spawn(test2)
g1.join()
g2.join()
运行结果如下:
task1 0
task2 0
task1 1
task2 1
task1 2
task2 2
task1 3
task2 3
task1 4
task2 4
task1 5
task2 5
task1 6
task2 6
task1 7
task2 7
task1 8
task2 8
task1 9
task2 9
可以看出是并发的
实例2:
import time
import gevent
from gevent import monkey
monkey.patch_all() # 加入这两行就可以在gevent中调用time
def test1():
for i in range(10):
# gevent.sleep(1)
time.sleep(1)
print('task1',i)
def test2():
for i in range(10):
# gevent.sleep(1)
time.sleep(1)
print('task2',i)
g1=gevent.spawn(test1)
g2=gevent.spawn(test2)
g1.join()
g2.join()
结果如下:
task1 0
task2 0
task1 1
task2 1
task1 2
task2 2
task1 3
task2 3
task1 4
task2 4
task1 5
task2 5
task1 6
task2 6
task1 7
task2 7
task1 8
task2 8
task1 9
task2 9