- 多任务是指操作系统可以同时运行多个程序。比如你可以在一台电上同时听歌,用pycharm写程序还可以用谷歌浏览器上网。
现在,多核CPU已经非常普及了,但是,即使过去的单核CPU,也可以执行多任务。由于CPU执行代码都是顺序执行的,那么,单核CPU是怎么执行多任务的呢?
答案就是操作系统轮流让各个任务交替执行,任务1执行0.01秒,切换到任务2,任务2执行0.01秒,再切换到任务3,执行0.01秒……这样反复执行下去。表面上看,每个任务都是交替执行的,但是,由于CPU的执行速度实在是太快了,我们感觉就像所有任务都在同时执行一样。
真正的并行执行多任务只能在多核CPU上实现,但是,由于任务数量远远多于CPU的核心数量,所以,操作系统也会自动把很多任务轮流调度到每个核心上执行。
- 并发:假的多任务
- 并行:真的多任务
并发:指的是任务数多余cpu核数,通过操作系统的各种任务调度算法,实现用多个任务“一起”执行(实际上总有一些任务不在执行,因为切换任务的速度相当快,看上去一起执行而已)
并行:指的是任务数小于等于cpu核数,即任务真的是一起执行的
yy_01_没有多任务的程序
import time
def sing():
"""唱歌 5秒钟"""
for i in range(5):
print("---正在唱歌---")
time.sleep(1)
def dance():
"""跳舞 5秒钟"""
for i in range(5):
print("---正在跳舞---")
time.sleep(1)
def main():
sing()
dance()
if __name__ == '__main__':
main()
yy_02_使用多任务(线程)的程序
import time
import threading
def sing():
"""唱歌 5秒钟"""
for i in range(5):
print("---正在唱歌---")
time.sleep(1)
def dance():
"""跳舞 5秒钟"""
for i in range(5):
print("---正在跳舞---")
time.sleep(1)
def main():
t1 = threading.Thread(target=sing)
t2 = threading.Thread(target=dance)
t1.start()
t2.start()
if __name__ == '__main__':
main()
yy_03_让某些线程先执行
import threading
import time
def test1():
for i in range(5):
print("1----%d--1" % i)
def test2():
for i in range(5):
print("2----%d--2" % i)
def main():
t1 = threading.Thread(target=test1)
t2 = threading.Thread(target=test2)
t1.start()
time.sleep(1)
print("---1---")
t2.start()
time.sleep(1)
print("---2---")
print("执行完毕")
if __name__ == '__main__':
main()
yy_04_循环查看当前运行的线程
import threading
import time
def test1():
for i in range(5):
print("1----%d--1" % i)
time.sleep(1)
# 如果创建Thread时执时的函数,运行结束,那么就意味着,这个子线程结束了。
def test2():
for i in range(10):
print("2----%d--2" % i)
time.sleep(1)
def main():
t1 = threading.Thread(target=test1)
t2 = threading.Thread(target=test2)
t1.start()
t2.start()
while True:
# enumerate()打印下标
if len(threading.enumerate()) <= 1:
break
print(threading.enumerate())
time.sleep(1)
if __name__ == '__main__':
main()
yy_05_验证创建线程以及运行时间
import threading
import time
def test():
for i in range(5):
print("1----%d----1" % i)
time.sleep(1)
def main():
# 验证什么时候开始创建子线程
# 测试1:在创建Thread之前打印线程数
print(threading.enumerate())
t1 = threading.Thread(target=test)
# 测试2:在创建Thread之后打印线程数
print(threading.enumerate())
t1.start()
# 测试3:在start之后打印线程数
while True:
if len(threading.enumerate()) <= 1:
break
print(threading.enumerate())
time.sleep(1)
if __name__ == '__main__':
main()
- 未完待续
yy_06_函数里面修改全局变量
gl_num = 100
gl_list = [1, 2]
gl_tuple = (1, 2)
def test():
global gl_num
gl_num += 100
def test2():
gl_list.append(666)
def test3():
global gl_tuple
gl_tuple = (1, 2, 777)
print(gl_num)
print(gl_list)
print(gl_tuple)
test()
test2()
test3()
print(gl_num)
print(gl_list)
print(gl_tuple)
yy_07_多线程共享全局变量
import threading
gl_num = 100
def test1():
global gl_num
gl_num += 1
print("---test1----gl_num = %d" % gl_num)
def test2():
print("---test2----gl_num = %d" % gl_num)
def main():
t1 = threading.Thread(target=test1)
t2 = threading.Thread(target=test2)
t1.start()
t2.start()
print(gl_num)
if __name__ == '__main__':
main()
yy_08_args方法
import threading
gl_list = [1, 2]
def test1(lis):
lis.append(3)
print("---test1----gl_list = %s" % lis)
def test2(lis):
print("---test2----gl_list = %s" % lis)
def main():
t1 = threading.Thread(target=test1, args=(gl_list,)) # 元组只有一个参数要加,号
t2 = threading.Thread(target=test2, args=(gl_list,))
t1.start()
t2.start()
print(gl_list)
if __name__ == '__main__':
main()
yy_09_共享全局变量问题-资源竞争
import threading
import time
gl_num = 0
def test1(lis):
global gl_num
for i in range(lis):
gl_num += 1 # 底层语言运行这句代码时,可分为三部
# 1.读取gl_num的值
# 2.进行+1操作
# 3.保存结果
# 程序运行过程中,可能存在,当线程1运行到 2 时,准备运行 3 保存
# 结果这时线程2,开始抢着运行,这会导致线程1进行了 2(+1操作)却没有运行3(保存),这时gl_num的值还是0
# 当线程2运行结束,这时线程1会接着运行刚才没有运行的3,这会导致本次循环没有进行+1操作
# 仅仅只是保存了上次的结果,所以会导致运行结果错误。
print("---test1----gl_list = %d" % gl_num)
def test2(lis):
global gl_num
for i in range(lis):
gl_num += 1
print("---test1----gl_list = %d" % gl_num)
def main():
t1 = threading.Thread(target=test1, args=(1000000,))
t2 = threading.Thread(target=test2, args=(1000000,))
t1.start()
t2.start()
# 等3秒让上面两个线程执行完毕
time.sleep(3)
print("---main-----gl_num = %d---" % gl_num)
if __name__ == '__main__':
main()
yy_10_使用互斥锁解决资源竞争问题
import threading
import time
gl_num = 0
# 创建一个互斥锁,默认是没有上锁的
mutex = threading.Lock()
def test1(lis):
global gl_num
# 上锁,如果之前没有上锁,上锁成功
# 如果上锁之前,已经被上锁,那么此时会堵塞在这里,直到这个锁被解开
mutex.acquire()
for i in range(lis):
gl_num += 1
# 解锁 (1000000次循环遍历运行结束才会解锁)
mutex.release()
print("---test1----gl_list = %d" % gl_num)
def test2(lis):
global gl_num
# 上锁
mutex.acquire()
for i in range(lis):
gl_num += 1
# 解锁
mutex.release()
print("---test2----gl_list = %d" % gl_num)
def main():
t1 = threading.Thread(target=test1, args=(1000000,))
t2 = threading.Thread(target=test2, args=(1000000,))
t1.start()
t2.start()
# 等3秒让上面两个线程执行完毕
time.sleep(2)
print("---main-----gl_num = %d---" % gl_num)
if __name__ == '__main__':
main()
yy_11_使用互斥锁解决资源竞争问题(方法2)
import threading
import time
gl_num = 0
# 创建一个互斥锁,默认是没有上锁的
mutex = threading.Lock()
def test1(lis):
global gl_num
for i in range(lis):
# 上锁的代码越少越好
# 上个方法太狠,把锁加在for循环上面,会导致直到这个循环遍历运行完毕才会解锁
# 问题仅仅是出在了这句代码上,只要让它把底层的3个步骤运行完毕,就可以解锁了
mutex.acquire()
gl_num += 1
# 解锁
mutex.release()
print("---test1----gl_list = %d" % gl_num)
def test2(lis):
global gl_num
for i in range(lis):
# 上锁
mutex.acquire()
gl_num += 1
# 解锁
mutex.release()
print("---test2----gl_list = %d" % gl_num)
def main():
t1 = threading.Thread(target=test1, args=(1000000,))
t2 = threading.Thread(target=test2, args=(1000000,))
t1.start()
t2.start()
# 等3秒让上面两个线程执行完毕
time.sleep(3)
print("---main-----gl_num = %d---" % gl_num)
if __name__ == '__main__':
main()
类里面使用线程的固定方法(把要执行线程的程序写到run方法中就可以了):
import threading
import time
class MyThread(threading.Thread):
def run(self):
for i in range(3):
time.sleep(1)
msg = "I'm "+self.name+' @ '+str(i) #name属性中保存的是当前线程的名字
print(msg)
if __name__ == '__main__':
t = MyThread()
t.start()