进程-内存分配单元
线程-CPU调度单元(箭头),操作系统真正调动的是线程
主线程必须等子线程结束后才会结束。
进程和线程之间的一个区别是:进程之间不共享全局变量,而线程之间共享全局变量,这是线程的一个优点,即不需要像进程一样单独通过队列等方式来共享变量,而是直接共享,但也是一个缺点。
# coding=utf-8
from threading import Thread
import time
g_num = 0
def test1():
global g_num
for i in range(1000000):
g_num += 1
print("---test1---g_num=%d"%g_num)
def test2():
global g_num
for i in range(1000000):
g_num += 1
print("---test2---g_num=%d"%g_num)
p1 = Thread(target=test1)
p1.start()
#time.sleep(3) #取消屏蔽之后 再次运行程序,结果会不一样,,,为啥呢?
p2 = Thread(target=test2)
p2.start()
print("---g_num=%d---"%g_num)
# ---g_num=296748---
# ---test2---g_num=1348783
# ---test1---g_num=1776224
# 取消屏蔽之后
# ---test1---g_num=1000000
# ---g_num=1207917---
# ---test2---g_num=2000000
在屏蔽的情况下,为什么会出现g_num不到1000000呢?看下图
对于每个线程,加1和赋值操作是分开的两步,可能会出现如下情况:cpu先调用线程1进行加1操作,但是还没来得及进行赋值便调用线程2执行相同的加1操作,而后再分别调用1和2进行赋值操作,这期间进行了两次加1,但实际上只加了1,所以g_num最终不到2000000
如何避免上述情况,使得各线程之间不受到影响?加条件语句g_flag
这样子可以避免加1操作进行重复,但是这样就好了吗?
这样结果虽然避免了各线程之间受影响,但是效率却很低,因为g_flag=1时,满足线程1的条件,期间CPU调用线程2时,一直在判断是否满足条件g_flag !=1,这样就占用了CPU资源,做了很多无用功。
那应该如何提高CPU利用率呢?给每个线程上互斥锁
#创建锁
mutex= threading.Lock()
#锁定
mutex.acquire()
#释放
mutex.release()
一旦给一个进程上了锁,其他进程只能等待,相当于睡觉,不会占用CPU资源,因此效率会提高很多。
# coding=utf-8
from threading import Thread, Lock
import time
g_num = 0
def test1():
global g_num
#这个线程和test2线程都在抢着 对这个锁 进行上锁,如果有1方成功的上锁,那么导致另外
#一方会堵塞(一直等待)到这个锁被解开为止
mutex.acquire()
for i in range(1000000):
g_num += 1
mutex.release()#用来对mutex指向的这个锁 进行解锁,,,只要开了锁,那么接下来会让所有因为
#这个锁 被上了锁 而堵塞的线程 进行抢着上锁
print("---test1---g_num=%d"%g_num)
def test2():
global g_num
mutex.acquire()
for i in range(1000000):
g_num += 1
mutex.release()
print("---test2---g_num=%d"%g_num)
#创建一把互斥锁,这个锁默认是没有上锁的
mutex = Lock()
p1 = Thread(target=test1)
p1.start()
#time.sleep(3) #取消屏蔽之后 再次运行程序,结果会不一样,,,为啥呢?
p2 = Thread(target=test2)
p2.start()
print("---g_num=%d---"%g_num)
# ---g_num=653790---
# ---test1---g_num=1000000
# ---test2---g_num=2000000
注意:
多线程上锁,是为了让多个线程一个一个访问他们共有的资源,上锁之后,类似于单线程。那所谓的并行是指的什么呢?只指在多核的情况下,只有多核的时候,才能同时执行多个任务。
线程的同步
同步使得线程可以有序的去执行
# coding=utf-8
from threading import Thread,Lock
from time import sleep
class Task1(Thread):
def run(self):
while True:
if lock1.acquire():
print("------Task 1 -----")
sleep(0.5)
lock2.release()
class Task2(Thread):
def run(self):
while True:
if lock2.acquire():
print("------Task 2 -----")
sleep(0.5)
lock3.release()
class Task3(Thread):
def run(self):
while True:
if lock3.acquire():
print("------Task 3 -----")
sleep(0.5)
lock1.release()
#使用Lock创建出的锁默认没有“锁上”
lock1 = Lock()
#创建另外一把锁,并且“锁上”
lock2 = Lock()
lock2.acquire()
#创建另外一把锁,并且“锁上”
lock3 = Lock()
lock3.acquire()
t1 = Task1()
t2 = Task2()
t3 = Task3()
t1.start()
t2.start()
t3.start()
# ------Task 1 -----
# ------Task 2 -----
# ------Task 3 -----
# ------Task 1 -----
# ------Task 2 -----
# ------Task 3 -----
# ------Task 1 -----
# ------Task 2 -----
总结
锁的好处:
确保了某段关键代码只能由⼀个线程从头到尾完整地执行。
锁的坏处:
阻⽌了多线程并发执⾏,包含锁的某段代码实际上只能以单线程模式执
⾏,效率就⼤⼤地下降了
由于可以存在多个锁,不同的线程持有不同的锁,并试图获取对⽅持有
的锁时,可能会造成死锁