目录
多任务:
多任务处理是指用户可以在同一时间内运行多个应用程序,每个应用程序被称作一个任务.
并发:
指的是任务数多余cpu核数,通过操作系统的各种任务调度算法,实现用多个任务“一起”执行(实际上总有一些任务不在执行,因为切换任务的速度相当快,看上去一起执行而已)
并行:
指的是任务数小于等于cpu核数,即任务真的是一起执行的
多线程(ultithreading):
是指从软件或者硬件上实现多个线程并发执行的技术。具有多线程能力的计算机因有硬件支持而能够在同一时间执行多于一个线程,进而提升整体处理性能。具有这种能力的系统包括对称多处理机、多核心处理器以及芯片级多处理(Chip-level multithreading)或同时多线程(Simultaneous multithreading)处理器。 [1] 在一个程序中,这些独立运行的程序片段叫作“线程”(Thread),利用它编程的概念就叫作“多线程处理(Multithreading)”。具有多线程能力的计算机因有硬件支持而能够在同一时间执行多于一个线程,进而提升整体处理性能。
多线程代码演示:
"""
明确程序的目标:
1.验证多线程、(多个任务,同时执行,)
2.主线程在子线程处理完成后才死亡
步骤流程:
1.创建两个函数
2.在main函数中创建两个线程,分别去执行这两个函数。
3.在主线程中实时检测线程的数量,如果线程的数量为1,则子线程执行完成
"""
import threading
import time
def say_yes():
for i in range(5):
print("第%d行:yes,yes,yes,yes,yes." % i)
time.sleep(0.5)
def say_no():
for i in range(5):
print("第%d行:no,no,no,no,no." % i)
time.sleep(0.5)
def main():
# 创建两个子线程
t1 = threading.Thread(target = say_yes)
t2 = threading.Thread(target = say_no)
# 开始执行两个子线程
t1.start()
t2.start()
# 查看当前的线程
print(threading.enumerate())
while True:
if len(threading.enumerate()) == 1:
print("子线程完成")
break
else:
pass
if __name__ == "__main__":
main()
在类中实现多继承:继承threading.Thread类:
"""
程序的主要目标是:在类中实现多线程
主要流程:
1.创建一个类,继承threading.Thread类
2.然后重写run方法,当run方法中的代码执行完成时线程结束。
3.在主函数中创建多个类对象,让他们分别执行,然后查看效果
注意:
1.每个线程默认会有一个名字:self.name
2.无法控制线程的调度顺序
3.当执行一个线程遇到堵塞的时候,操作系统会马上切换另一个线程继续执行。
4.当线程或者进程高于cpu核数的时候:其实就是所谓的并发,就是在极短的时间内来回的切换任务
"""
import threading
import time
class CreateThread(threading.Thread):
@staticmethod
def sing():
print("i can singing in English")
@staticmethod
def dance():
print("i can dance")
def run(self):
for i in range(3):
self.sing()
self.dance()
print("我的线程号:",self.name)
time.sleep(0.5)
def main():
for i in range(5):
t = CreateThread()
t.start()
if __name__ == "__main__":
main()
多线程共享全局变量:
"""
程序的目标:验证多线程共享全局变量的特性
主要步骤:
1.定义一个全局变量
2.在一个函数中对全局变量进行运算
3.在另一个函数中对这个全局变量进行显示
"""
import threading
import time
g_num = 0
def caltulate_num():
global g_num
print("将1~10的累加的结果保存在g_mun中")
for i in range(10):
g_num += i
def show_num():
global g_num
print("经过计算后全局变量的值为:", g_num)
def main():
t1 = threading.Thread(target=caltulate_num)
t2 = threading.Thread(target=show_num)
t1.start()
# 等待一下,等待完成计算
time.sleep(2)
t2.start()
if __name__ == "__main__":
main()
多线程共享全局变量的缺点:
"""
程序目标:演示多线共享全局变量的缺点
假设:两个函数分别对一个全局变量进行加1操作,分别进行100000次
有可能出现这样的情况:当线程A,取出全局变量的值时,还没有进行加1,此时cpu切换线程换到线程B
而线程B执行的时候,也刚好取出全局变量,还没有进程加1,此时有切换线程到线程A
假设他们取出来的都是5 线程A进行+1 变成6 ,此时执行线程B,而它已经取完了,值还是5,
它进行+1,结果为6.........所以本来结果应该为7的
"""
import threading
g_num = 0
def calculate_1(num):
global g_num
for i in range(num):
g_num += 1
print("经过cal_1函数的计算后全局变量的值为: ", g_num)
def calculate_2(num):
global g_num
for i in range(num):
g_num += 1
print("经过cal_2函数的计算后全局变量的值为: ", g_num)
def main():
global g_num
t1 = threading.Thread(target=calculate_1, args=(100000,))
t1.start()
t2 = threading.Thread(target=calculate_2,args=(100000,))
t2.start()
print("经过两个函数的计算后全局变量的值为: ", g_num)
if __name__ == "__main__":
main()
解决共享全局变量出现的问题:互斥锁
"""
程序的主要目标:利用线程同步来解决共享全局变量的问题:
需要了解的知识点:
同步的概念:(同:协同)也就是说先执行其中的一个,在执行另外一个。说白了那部分代码执行就是单线程
互斥锁:互斥锁为资源引入了一状态:锁定/非锁定
解决的主要过程:
当一个线程使用全局变量的时候,将其锁定,这样就使得其他线程不可以使用,直到这个线程释放该资源,此时该资源状态为非锁定
其他线程才能利用。
解决实现:
threading中提供了LOCK类。
核心步骤为:
1.创建一个锁:mutex = threading.Lock()
2.上锁:mutex.acquire() 如果在上锁前已经被锁定,这里就会产生堵塞。
3.解锁:mutex.release()
锁的好处:
确保了某段关键代码只能由一个线程从头到尾完整地执行
锁的坏处:
阻止了多线程并发执行,包含锁的某段代码实际上只能以单线程模式执行,效率就大大地下降了
由于可以存在多个锁,不同的线程持有不同的锁,并试图获取对方持有的锁时,可能会造成死锁
"""
import threading
g_num = 0
metex = threading.Lock()
def calculate_1(num):
global g_num
for i in range(num):
metex.acquire()
g_num += 1
metex.release()
print("经过cal_1函数的计算后全局变量的值为: ", g_num)
def calculate_2(num):
global g_num
for i in range(num):
metex.acquire()
g_num += 1
metex.release()
print("经过cal_2函数的计算后全局变量的值为: ", g_num)
def main():
global g_num
t1 = threading.Thread(target=calculate_1, args=(10000,))
t2 = threading.Thread(target=calculate_2,args=(10000,))
t1.start()
t2.start()
# 等待计算完成才能打印出正确的结果
while True:
if len(threading.enumerate()) == 1:
break
print("经过两个函数的计算后全局变量的值为: ", g_num)
if __name__ == "__main__":
main()
使用互斥锁出现死锁的情况举例:
"""
程序的目标:演示死锁
主要步骤:
1.创建两把互斥锁 A,B
2.在一个函数中,先用A上锁,等待一会,在用B上锁
3.在另一个函数中 ,先用B上锁,等待一会,在用A上锁
4.在main()函数中:创建两个线程分别执行这两个函数,此时就会出现互相等待对方解锁的情况,以至于程序一直在堵塞的状态
注意:在真正的情况中要避免出现死锁的现象
可以了解一个有趣的算法:银行家算法
"""
import threading
import time
def fun1():
metex1.acquire()
print("在函数1中用锁1上锁")
# 等待1s为了让另一个线程,先上下面这个锁
time.sleep(1)
metex2.acquire()
print("下面的代码都不会执行")
metex2.release()
metex1.release()
def fun2():
metex2.acquire()
print("在函数2中用锁2上锁")
# 等待1s为了让另一个线程,先上下面这个锁
time.sleep(1)
metex1.acquire()
print("下面的代码不会执行")
metex1.release()
metex2.release()
metex1 = threading.Lock()
metex2 = threading.Lock()
def main():
t1 = threading.Thread(target=fun1)
t2 = threading.Thread(target=fun2)
t1.start()
t2.start()
if __name__ == "__main__":
main()