- 版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/kun1280437633/article/details/80320488
1、什么是并发和并行
并发:指的是任务数多余cpu核数,通过操作系统的各种任务调度算法,实现用多个任务“一起”执行(实际上总有一些任务不在执行,因为切换任务的速度相当快,看上去一起执行而已)
并行:指的是任务数小于等于cpu核数,即任务真的是一起执行的
2、 python的thread模块是比较底层的模块,python的threading模块是对thread做了一些包装的,可以更加方便的被使用
示例代码:
# 当程序开始执行,解释器会自动创建主线程,执行程序代码
import threading # 1. 导入 threading 模块
import time
def work1():
for i in range(2000):
print('正在搬砖:', i)
def work2():
for i in range(2000):
print('正在扫地:', i)
def main():
"""开启线程,实现多任务执行"""
print('main--start---',threading.enumerate())
t1 = threading.Thread(target=work1) # 当创建新子线程对象的时候,线程没有开始运行
print('创建子线程对象---',threading.enumerate())
t2 = threading.Thread(target=work2)
t1.start() # 当调用 start 之后,线程才开始运行
print('子线程开始运行---',threading.enumerate())
t2.start()
#如果主线程被杀死时还有子线程在运行,则所有子线程会一起死掉
print("结束啦")
if __name__ == '__main__':
main()
注意:(1)主线程会等待子线程全部结束后才会结束,主线程死亡,子线程立即死亡
(2)多个任务之间会争夺CPU资源,什么时候能抢到时间片是随机的,每一次争夺到的时间片长度都是随机的
(3)多任务的切换是无序的
3、自定义类实现线程
- python的threading.Thread类有一个run方法,用于定义线程的功能函数,可以在自己的线程类中覆盖该方法。而创建自己的线程实例后,通过Thread类的start方法,可以启动该线程,交给python虚拟机进行调度,当该线程获得执行的机会时,就会调用run方法执行线程。
代码:
# 当程序开始执行,解释器会自动创建主线程,执行程序代码
import threading # 1. 导入 threading 模块
import time
class KHThread(threading.Thread): # 1.定义类
def run(self): # 2.提供 run 方法
for i in range(5):
print('正在搬砖:', i)
time.sleep(0.5)
def main():
"""开启线程,实现多任务执行"""
# 3.创建线程对象
# t1 = threading.Thread(target=work1)
t1 = KHThread()
# 4.开启线程,执行 run 函数
t1.start()
print(t1) # 自定线程类,可以方便的了解到线程提供了什么功能
if __name__ == '__main__':
main()
4、共享数据
- 在一个进程内的所有线程共享全局变量,很方便在多个线程间共享数据
- 缺点就是,线程是对全局变量随意遂改可能造成多线程之间对全局变量的混乱(即线程非安全)
代码:
import threading
import time
def work1(tmp):
tmp.append(11) # t1 线程修改列表数据
#tmp["nam"] = "gaohan"
print('work1--', tmp)
def work2(tmp):
print('work2--', tmp) # t2 线程可以获取到数据
def main():
"""两个线程间通过传参共享数据"""
ls = []
t1 = threading.Thread(target=work1, args=(ls,)) # 向线程传参,使用 args 关键字参数,传递的值必须是元组
t2 = threading.Thread(target=work2, args=(ls,))
t1.start()
time.sleep(1)
t2.start()
if __name__ == '__main__':
main()
结果:
work1-- [11]
work2-- [11]
出现问题的代码:
import threading
def work1():
global num
for i in range(1000000):
num += 1
print('work1--', num)
def work2():
global num
for i in range(1000000):
num += 1
print('work2--', num)
num = 0
def main():
"""开启两个线程,修改同一个全局变量"""
t1 = threading.Thread(target=work1)
t2 = threading.Thread(target=work2)
t1.start()
t2.start()
if __name__ == '__main__':
main()
结果:
work1-- 1335864
work2-- 1387952
5、使用互斥锁解决线程间的资源竞争
import threading
"""
1. 要达到多个线程同时运行的效果,上锁的代码范围要尽量的小
2. 大量的加锁和解锁比较消耗时间,应该尽量减少加锁和解锁的次数
"""
def work1(lock):
global num
for i in range(1000000):
lock.acquire() # 获取锁
num += 1
if i%10000 == 0:
print('work1---', i)
lock.release() # 释放锁
print('work1--', num)
def work2(lock):
global num
for i in range(1000000):
lock.acquire() # 获取锁
num += 1
if i%10000 == 0:
print('work2---', i)
lock.release() # 释放锁
print('work2--', num)
num = 0
def main():
"""开启两个线程,修改同一个全局变量"""
lock = threading.Lock() # 创建锁
t1 = threading.Thread(target=work1, args=(lock,))
t2 = threading.Thread(target=work2, args=(lock,))
t1.start()
t2.start()
if __name__ == '__main__':
main()
6、死锁
#coding=utf-8
import threading
import time
class MyThread1(threading.Thread):
def run(self):
# 对mutexA上锁
mutexA.acquire()
# mutexA上锁后,延时1秒,等待另外那个线程 把mutexB上锁
print(self.name+'----do1---up----')
time.sleep(1)
# 此时会堵塞,因为这个mutexB已经被另外的线程抢先上锁了
mutexB.acquire()
print(self.name+'----do1---down----')
mutexB.release()
# 对mutexA解锁
mutexA.release()
class MyThread2(threading.Thread):
def run(self):
# 对mutexB上锁
mutexB.acquire()
# mutexB上锁后,延时1秒,等待另外那个线程 把mutexA上锁
print(self.name+'----do2---up----')
time.sleep(1)
# 此时会堵塞,因为这个mutexA已经被另外的线程抢先上锁了
mutexA.acquire()
print(self.name+'----do2---down----')
mutexA.release()
# 对mutexB解锁
mutexB.release()
mutexA = threading.Lock()
mutexB = threading.Lock()
if __name__ == '__main__':
t1 = MyThread1()
t2 = MyThread2()
t1.start()
t2.start()