并行:真的多任务
并发:假的多任务
如果有8个程序:一定是并发(cpu 核数数量小于任务数)
threading模块有个类叫thread\
一、创建一个对象,对象.start启动线程
import threading
import time
def saysorry():
print("i am sorry")
time.sleep(1)
if __name__ == "__main__":
for i in range(5):
t = threading.Thread(target=saysorry)
t.start() # 启动线程,让线程开始执行
一个程序运行完成之后,有一个执行代码的东西,这个东西叫做线程
二、查看所有线程
threading.enumerate() 获取所有的数量
验证让某些进程先执行
# 查看线程数
import threading
def test1():
for i in range(5):t1 = threading.Thread(target=test1)
print("---test1---%d---"% i)
def test2():
for i in range(5):
print("---test2---%d---"% i)
def main():
t1 = threading.Thread(target=test1)
t2 = threading.Thread(target=test1)
t1.start()
time.sleep(1)
t2.start()
time.sleep(2)
print(len(threading.enumerate()))
if __name__ == "__main__":
main()
线程执行的顺序不一定
保证谁先执行可以增加time的睡眠时间
三、循环查看当前运行的线程
如果主线程先si了,子线程也会结束
# 查看线程数
import threading
import time
def test1():
for i in range(5):
print("---test1---%d---"% i)
time.sleep(1)
def test2():
for i in range(5):
print("---test2---%d---"% i)
time.sleep(1)
def main():
t1 = threading.Thread(target=test1)
t2 = threading.Thread(target=test2)
t1.start()
# time.sleep(1)
t2.start()
while True:
print(threading.enumerate())
if len(threading.enumerate()) <= 1:
break
time.sleep(1)
if __name__ == "__main__":
main()
四、验证创建线程以及运行时间
当调用thread之后不会创建线程,当调用实例方法start后才会调用线程,才能够执行
# 查看线程数
import threading
import time
def test1():
for i in range(5):
print("---test1---%d---"% i)
time.sleep(1)
def test2():
for i in range(5):
print("---test2---%d---"% i)
time.sleep(1)
def main():
print("调用前打印当前线程信息:", threading.enumerate())
t1 = threading.Thread(target=test1)
print("调用后打印当前线程信息:", threading.enumerate())
# t2 = threading.Thread(target=test2)
t1.start()
print("调用start后打印当前线程信息:", threading.enumerate())
# time.sleep(1)
# t2.start()
while True:
print(threading.enumerate())
if len(threading.enumerate()) <= 1:
break
time.sleep(1)
if __name__ == "__main__":
main()
输出:
调用前打印当前线程信息: [<_MainThread(MainThread, started 2712)>]
调用后打印当前线程信息: [<_MainThread(MainThread, started 2712)>]
---test1---0---调用start后打印当前线程信息: [<_MainThread(MainThread, started 2712)>, <Thread(Thread-1, started 4328)>]
[<_MainThread(MainThread, started 2712)>, <Thread(Thread-1, started 4328)>]
---test1---1---
[<_MainThread(MainThread, started 2712)>, <Thread(Thread-1, started 4328)>]
---test1---2---
[<_MainThread(MainThread, started 2712)>, <Thread(Thread-1, started 4328)>]
---test1---3---
[<_MainThread(MainThread, started 2712)>, <Thread(Thread-1, started 4328)>]
---test1---4---
[<_MainThread(MainThread, started 2712)>, <Thread(Thread-1, started 4328)>]
[<_MainThread(MainThread, started 2712)>]
1、调用start就可以执行
2、线程结束,函数结束
3、线程创建完不确定谁先执行,可以通过延时来保证谁先执行
4、主线程最后结束
定义类,类继承thread,定义run方法,也可以用
import threading
import time
class MyThread(threading.Thread):
def run(self):
for i in range(3):
time.sleep(1)
msg = "I am" + self.name + "@" + str(i)
print(msg)
if __name__ == "__main__":
t = MyThread()
t.start() # 必须定义run函数,不然没效果,如果run后面还有其它函数,那么久在run函数里面调用
五、多线程之间共享全局变量
在一个函数中,对全局变量进行修改的时候,是否需要global进行说明,要看是否对全局变量的指向进行修改
如果修改了执行,就是让全局变量指向一个新的地方,那么必须使用global,如果仅仅修改了指向空间的数据,不需要
# 在一个函数中,对全局变量进行修改的时候,是否需要global进行说明,要看是否对全局变量的指向进行修改
# 如果修改了执行,就是让全局变量指向一个新的地方,那么必须使用global,如果仅仅修改了指向空间的数据,不需要
num = 100
nums = [11, 22]
def test():
global num
num += 100
def test2():
nums.append(33) # 如果这里是nums2 += [100, 200]绝对不行
print(num)
print(nums)
test()
test2()
print(num)
print(nums)
子线程和子线程之间共享全局变量
import threading
import time
g_num = 100
def test1():
global g_num
g_num += 1
print(".....in test1 g_num=%d..."%g_num)
def test2():
print("--in test2 g_num=%d---"%g_num)
def main():
t1 = threading.Thread(target=test1)
t2 = threading.Thread(target=test2)
t1.start()
time.sleep(1)
t2.start()
time.sleep(1)
print("---in main Thread g_num=%d---"%g_num)
if __name__ == "__main__":
main()
输出:
.....in test1 g_num=101...
--in test2 g_num=101---
---in main Thread g_num=101---
验证多线程共享全局变量
为什么共享:因为多任务往往配合使用
import threading
import time
g_num = 100
def test1(temp):
temp.append(33)
print(".....in test1 g_num=%s..."%str(temp))
def test2(temp):
print("--in test2 g_num=%s---"%str(temp))
g_nums = [11, 22]
def main():
# target 指定将来这个线程去哪个函数执行代码
# args指定将来调用函数的时候,传递什么数据过去
t1 = threading.Thread(target=test1, args = (g_nums, ))
t2 = threading.Thread(target=test2, args = (g_nums, ))
t1.start()
time.sleep(1)
t2.start()
time.sleep(1)
print("---in main Thread g_num=%s---"%str(g_nums))
if __name__ == "__main__":
main()
输出:
.....in test1 g_num=[11, 22, 33]...
--in test2 g_num=[11, 22, 33]---
---in main Thread g_num=[11, 22, 33]---
多线程开发可能遇到的问题
import threading
import time
g_num = 0
def test1(num):
global g_num
for i in range(num):
g_num += 1
print(".....in test1 g_num=%d..."%g_num)
def test2(num):
global g_num
for i in range(num):
g_num += 1
print("--in test2 g_num=%d---"%g_num)
def main():
# target 指定将来这个线程去哪个函数执行代码
# args指定将来调用函数的时候,传递什么数据过去
t1 = threading.Thread(target=test1, args = (100,))
t2 = threading.Thread(target=test2, args = (100,))
t1.start()
t2.start()
# 等待上面的两个线程执行完毕
time.sleep(5)
print("---in main Thread g_num=%d---"%g_num)
if __name__ == "__main__":
main()
输出:
.....in test1 g_num=100...
--in test2 g_num=200---
---in main Thread g_num=200---
有可能会形成资源竞争
import threading
import time
g_num = 0
def test1(num):
global g_num
for i in range(num):
g_num += 1
print(".....in test1 g_num=%d..."%g_num)
def test2(num):
global g_num
for i in range(num):
g_num += 1
print("--in test2 g_num=%d---"%g_num)
def main():
# target 指定将来这个线程去哪个函数执行代码
# args指定将来调用函数的时候,传递什么数据过去
t1 = threading.Thread(target=test1, args = (1000000,))
t2 = threading.Thread(target=test2, args = (1000000,))
t1.start()
t2.start()
# 等待上面的两个线程执行完毕
time.sleep(5)
print("---in main Thread g_num=%d---"%g_num)
if __name__ == "__main__":
main()
输出:
.....in test1 g_num=1378212...
--in test2 g_num=1618661---
---in main Thread g_num=1618661---
解决办法:
同步:协同一起走,按预定的次序进行
互斥锁
互斥锁:保证一个人操作的时候其他人进不去
import threading
import time
g_num = 0
def test1(num):
global g_num
# 如果上锁之前没有上锁,那么上锁成功
# 如果上锁之前,已经被上锁了,那么此时会堵塞在这里,指导这个锁解开为止
mutex.acquire()
for i in range(num):
g_num += 1
# 解锁
mutex.release()
print(".....in test1 g_num=%d..."%g_num)
def test2(num):
global g_num
mutex.acquire()
for i in range(num):
g_num += 1
mutex.release()
print("--in test2 g_num=%d---"%g_num)
# 创建一个互斥锁,默认是没有上锁的
mutex = threading.Lock()
def main():
# target 指定将来这个线程去哪个函数执行代码
# args指定将来调用函数的时候,传递什么数据过去
t1 = threading.Thread(target=test1, args = (1000000,))
t2 = threading.Thread(target=test2, args = (1000000,))
t1.start()
t2.start()
# 等待上面的两个线程执行完毕
time.sleep(5)
print("---in main Thread g_num=%d---"%g_num)
if __name__ == "__main__":
main()
输出
.....in test1 g_num=1000000...
--in test2 g_num=2000000---
---in main Thread g_num=2000000---
放在for里面
def test1(num):
global g_num
# 如果上锁之前没有上锁,那么上锁成功
# 如果上锁之前,已经被上锁了,那么此时会堵塞在这里,指导这个锁解开为止
for i in range(num):
mutex.acquire()
g_num += 1
mutex.release()
# 解锁
print(".....in test1 g_num=%d..."%g_num)
def test2(num):
global g_num
for i in range(num):
mutex.acquire()
g_num += 1
mutex.release()
print("--in test2 g_num=%d---"%g_num)
# 创建一个互斥锁,默认是没有上锁的
mutex = threading.Lock()
def main():
# target 指定将来这个线程去哪个函数执行代码
# args指定将来调用函数的时候,传递什么数据过去
t1 = threading.Thread(target=test1, args = (1000000,))
t2 = threading.Thread(target=test2, args = (1000000,))
t1.start()
t2.start()
# 等待上面的两个线程执行完毕
time.sleep(5)
print("---in main Thread g_num=%d---"%g_num)
if __name__ == "__main__":
main()
输出
.....in test1 g_num=1981457...--in test2 g_num=2000000---
---in main Thread g_num=2000000---
避免死锁
1、添加超时时间
2、程序设计的时候避免
进程
不运行程序 运行后进程
进程和线程的对比:
进程是资源分配的单位
线程是cpu调度和执行的单位
# 进程池Pool
from multiprocessing import Pool
import os, time, random
def worker(msg):
t_start = time.time()
print("%s开始执行,进程号为%d"%(msg, os.getpid()))
# random.random()随机生成0-1之间的浮点数
time.sleep(random.random()*2)
t_stop = time.time()
print(msg, "执行完毕,耗时%0.2f"%(t_stop-t_start))
def main():
po = Pool(3) # 定义一个进程池,最大进程数3
for i in range(0, 10):
# pool().apply_async(要调用的,目标,(传递给目标的参数元祖,))
# 每次循环将会用空闲出来的子进程去调用目标
po.apply_async(worker, (i,))
print("---start---")
po.close() # 关闭进程池,关闭后po不再接收新的请求
po.join() # 等待po中所有子进程执行完成,必须放在close之后
print("---end---")
if __name__ == "__main__":
main()
输出:
---start---
0开始执行,进程号为628
1开始执行,进程号为5856
2开始执行,进程号为4740
1 执行完毕,耗时0.59
3开始执行,进程号为5856
0 执行完毕,耗时0.67
4开始执行,进程号为628
2 执行完毕,耗时1.16
5开始执行,进程号为4740
4 执行完毕,耗时0.89
6开始执行,进程号为628
3 执行完毕,耗时1.47
7开始执行,进程号为5856
7 执行完毕,耗时0.02
8开始执行,进程号为5856
5 执行完毕,耗时1.61
9开始执行,进程号为4740
9 执行完毕,耗时0.50
6 执行完毕,耗时1.83
8 执行完毕,耗时1.54
---end---
多线程拷贝文件夹
import os
import multiprocessing
def copy_file(file_name, old_folder_name, new_folder_name):
"""完成文件的复制"""
print("模拟copy文件:从%s--->%s,文件名是:%s"%(old_folder_name, new_folder_name,file_name))
old_f = open(old_folder_name + "/" + file_name, "rb")
content = old_f.read()
old_f.close()
new_f = open(new_folder_name + "/" + file_name, "wb")
new_f.write(content)
new_f.close()
def main():
# 1、获取用户要copy的文件夹的名字
old_folder_name= input("请输入要copy的文件夹的名字:")
# 2、创建一个新的文件夹
new_folder_name = old_folder_name + "[复件]"
os.mkdir(new_folder_name)
# 3、获取文件夹中所有的带copy的文件名字 listdir()
try:
file_names = os.listdir((old_folder_name))
except:
pass
# 4、创建进程池
po = multiprocessing.Pool(5)
# 5、向进程池中添加拷贝文件的任务
for file_name in file_names:
po.apply_async(copy_file, args = (file_name,old_folder_name, new_folder_name))
# 复制原文件夹中的文件到新文件夹中的文件去
po.close()
po.join()
if __name__ == "__main__":
main()
输出:
请输入要copy的文件夹的名字:test
模拟copy文件:从test--->test[复件],文件名是:01.py
模拟copy文件:从test--->test[复件],文件名是:02.py
模拟copy文件:从test--->test[复件],文件名是:03.py
查看进度
import os
import multiprocessing
def copy_file(q, file_name, old_folder_name, new_folder_name):
"""完成文件的复制"""
print("模拟copy文件:从%s--->%s,文件名是:%s"%(old_folder_name, new_folder_name,file_name))
old_f = open(old_folder_name + "/" + file_name, "rb")
content = old_f.read()
old_f.close()
new_f = open(new_folder_name + "/" + file_name, "wb")
new_f.write(content)
new_f.close()
# 如果拷贝完了文件,那么久向队列中写入一个消息,表示已经完成
q.put(file_name)
def main():
# 1、获取用户要copy的文件夹的名字
old_folder_name= input("请输入要copy的文件夹的名字:")
# 2、创建一个新的文件夹
new_folder_name = old_folder_name + "[复件]"
os.mkdir(new_folder_name)
# 3、获取文件夹中所有的带copy的文件名字 listdir()
try:
file_names = os.listdir((old_folder_name))
except:
pass
# 4、创建进程池
po = multiprocessing.Pool(5)
# 5、创建一个队列
q = multiprocessing.Manager().Queue()
# 6、向进程池中添加拷贝文件的任务
for file_name in file_names:
po.apply_async(copy_file, args = (q, file_name,old_folder_name, new_folder_name))
# 复制原文件夹中的文件到新文件夹中的文件去
po.close()
# po.join()
count = 0
while True:
file_name = q.get()
# print("已经完成copy: %s"%file_name)
count += 1
print("\r完成的进度为:%.2f %%" % (count*100 / len(file_names)), end = "")
if count >= len(file_names):
break
if __name__ == "__main__":
main()
输出:
请输入要copy的文件夹的名字:test
模拟copy文件:从test--->test[复件],文件名是:02.py
模拟copy文件:从test--->test[复件],文件名是:03.py
模拟copy文件:从test--->test[复件],文件名是:01.py
完成的进度为:100.00 %