# 线程的并发是利用cpu上下文的切换(是并发,不是并行)
# 多线程执行的顺序是无序的
# 多线程共享全局变量
# 线程是继承在进程里的,没有进程就没有线程
# GIL全局解释器锁
# 只要在进行耗时的IO操做的时候,能释放GIL,因此只要在IO密集型的代码里,用多线程就很合适python
# 无序的,并发的算法
import threading
def test(n):
time.sleep(1)
print('task',n)
for i in range(10):
t = threading.Thread(target=test,args=('t_%s' % i,)) #注意,参数args后接的是一个元组,当只有一个参数时,要加一个逗号,不然运行会报错
t.start()编程
》》多线程
task t_0并发
task t_1app
task t_4async
task t_3函数
task t_2性能
task t_5ui
task t_7
task t_9
task t_8
task t_6
》》
import threading
num=0
def test1():
global num #局部变量只能使用全局变量,不可更改,更改须要加全局声明:‘global’
num+=100
def test2():
print(num)
t1 = threading.Thread(target=test1)
t2 = threading.Thread(target=test2)
t1.start()
t2.start()
import threading
import time
def test1(n):
time.sleep(1)
print('task', n)
def test2(n):
time.sleep(1)
print('task', n)
start = time.time()
l = []
t1 = threading.Thread(target=test1, args=(1,))
t2 = threading.Thread(target=test1, args=(2,))
t1.start()
t2.start()
l.append(t1)
l.append(t2)
for i in l:
i.join()
end = time.time()print(end - start)
GIL的全称是:Global Interpreter Lock,意思就是全局解释器锁,这个GIL并非python的特性,他是只在Cpython解释器里引入的一个概念,而在其余的语言编写的解释器里就没有这个GIL。例如:Jython,Pypy
为何会有gil?:
随着电脑多核cpu的出现核cpu频率的提高,为了充分利用多核处理器,进行多线程的编程方式更为普及,随之而来的困难是线程之间数据的一致性和状态同步,而python也利用了多核,因此也逃不开这个困难,为了解决这个数据不能同步的问题,设计了gil全局解释器锁。
说到gil解释器锁,咱们容易想到在多线程中共享全局变量的时候会有线程对全局变量进行的资源竞争,会对全局变量的修改产生不是咱们想要的结果,而那个时候咱们用到的是python中线程模块里面的互斥锁,哪样的话每次对全局变量进行操做的时候,只有一个线程可以拿到这个全局变量;看下面的代码:
import threading
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()
print(global_num)
print(global_num)
》》
104420
126353
》》
在上面的例子里,咱们建立了两个线程来争夺对global_num的加一操做,可是结果并不是咱们想要的。
当执行代码时,会并发三个线程:2个函数线程,一个main主线程(即运行代码的线程);由于其线程执行是无序的,就有可能加一操做的函数线程未完成,主线程就已经输出值,并且函数线程操做过大时发生的几率就更大;
解决方法:
在最后输出global_num前加入:
t1.join()
t2.join()
‘join’其效果就是等待线程执行完毕
当解决(1)问题后,其运行结果大约为100万多,并无变成200万,缘由以下图:
【解释】:假设当一次操做时,t1 线程拿到共享值num=20,其申请gil锁(不允许别人使用此值),接着进入cpu执行 +1 操做,但还未完成加一算法时,执行时间到了,被要求释放gil锁,此时t2开始运行(其运行初值num=20),进行相同操做返回num=21;接着运行t1以前未完成的操做(即给20进行+1操做),返回值依然为num=21;最终致使2次加一操做重复,而当这种操做过多时发生的次数也就越多,致使不能加到200万。
解决方法:咱们在这里加入互斥锁,使得我一个加值操做完成后,才可被其余线程使用共享变量,其锁住的代码段是串行的,但未锁段依然并发。
import threading
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
》》
CPU密集型也叫计算密集型,指的是系统的硬盘、内存性能相对CPU要好不少,此时,系统运做大部分的情况是CPU Loading 100%,CPU要读/写I/O(硬盘/内存),I/O在很短的时间就能够完成,而CPU还有许多运算要处理,CPU Loading很高。
在多重程序系统中,大部份时间用来作计算、逻辑判断等CPU动做的程序称之CPU bound。例如一个计算圆周率至小数点一千位如下的程序,在执行的过程中绝大部份时间用在三角函数和开根号的计算,即是属于CPU bound的程序。
CPU bound的程序通常而言CPU占用率至关高。这多是由于任务自己不太须要访问I/O设备,也多是由于程序是多线程实现所以屏蔽掉了等待I/O的时间。
IO密集型指的是系统的CPU性能相对硬盘、内存要好不少,此时,系统运做,大部分的情况是CPU在等I/O (硬盘/内存) 的读/写操做,此时CPU Loading并不高。
I/O bound的程序通常在达到性能极限时,CPU占用率仍然较低。这多是由于任务自己须要大量I/O操做,而pipeline作得不是很好,没有充分利用处理器能力。
一个程序运行起来以后,代码+用到的资源称之为进程,它是操做系统分配资源的基本单位,不只能够经过线程完成多任务,进程也是能够的。
#进程之间是相互独立的
#cpu密集的时候适合用多进程
#多进程比较耗费资源
import multiprocessing
import time
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__': #多进程必需要这一步
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
》》
import multiprocessing
num = 0
def test1():
global num
for i in range(10):
num += 1
def test2():
print(num)
if __name__ == '__main__':
p1 = multiprocessing.Process(target=test1)
p2 = multiprocessing.Process(target=test2)
p1.start()
p2.start()
》》
0
》》
import multiprocessing
from multiprocessing import Pool
import time
g_num = 0
def test1(n):
for i in range(n):
time.sleep(1)
print('test1', i)
def test2(n):
for i in range(n):
time.sleep(1)
print('test2', i)
def test3(n):
for i in range(n):
time.sleep(1)
print('test3', i)
def test4(n):
for i in range(n):
time.sleep(1)
print('test4', i)
if __name__ == '__main__':
pool = Pool(3) #把进程声明出来括号里不写东西说明无限制,若是写数字,就是最大的进程数
pool.apply_async(test1,(10,)) #用pool去调用函数test1,若是函数有参数,参数为10,格式为(10,)
pool.apply_async(test2,(10,))
pool.apply_async(test3,(10,))
pool.apply_async(test4,(10,))
pool.close() #close必须在join的前面
pool.join()
# 进程是资源分配的单位
# 线程是操做系统调度的单位
# 进程切换须要的资源最大,效率低
# 线程切换须要的资源通常,效率通常
# 协程切换任务资源很小,效率高
# 多进程、多线程根据cpu核数不同多是并行的,可是协程在一个线程中、
#协程碰见io就切换
import gevent,time
from gevent import monkey
monkey.patch_all()
def test1():
for i in range(10):
time.sleep(1)
print('test1', 1)
def test2():
for i in range(10):
time.sleep(2)
print('test2', 1)
g1 = gevent.spawn(test1)
g2 = gevent.spawn(test2)
g1.join()
g2.join()
=================================分割线==========================================