一、基础概念
1、并发vs并行
并发:指在一段时间内交替去执行任务
并行:指两个或多个时间在同一时刻执行

87f4c0ff7fbaa32a64fc2af2b35f061.png
2、进程vs线程
进程:一个正在运行的程序或者软件就是一个进程,它是操作系统进行资源分配的基本单位
一个进程默认有一个线程,进程里可以创建多个线程。程序启动默认会有一个主线程-
线程:线程是依附在进程里面的,没有进程就没有线程
1538058d53c0edb3d1e3e1e7575fec2.png 单线程示例:
import time
def play():
print("hello,welcome to play")
time.sleep(1)
print("play end")
def sleep():
print("hello,welcome to sleep")
time.sleep(2)
print("sleep end")
if __name__=='__main__':
play()
sleep()

image.png
二、多线程
1、线程的创建
class *threading.Thread(*group=None*, *target=None*, *name=None*, *args=()*, *kwargs={}*, ***, *daemon=None*)
group:线程组
target:执行目标的任务名
name:是线程名称。
args :以元组的形式给执行任务进行传参。 默认为 ()。
kwargs: 以字典的形式给执行任务进行传参。默认是 `{}`。
使用threading模块里面的Thread类创建出实例对象,然后通过start()方法真正的去产生一个新的线程
2、线程的常用方法
- setDaemon() 设置守护线程时,主线程执行完了,子线程也结束了
- join() 阻塞主线程 a.join() a为子线程,阻塞主线程,主线程会等待子线程a执行完了再执行下面
- start() 启动子线程
3、单线程vs多线程实例
- 单线程
import time
from threading import Thread
def play():
print("hello,welcome to play")
time.sleep(1)
print("play end")
t1 = time.time()
if __name__=='__main__':
for i in range(5):
play()
t2 = time.time()
print("耗时时长:%f"%(t2-t1))

image.png
- 多线程
import time
from threading import Thread
def play(name):
print(f"hello,{name} to play")
time.sleep(1)
print("play end")
t1 = time.time()
if __name__=='__main__':
for i in range(5):
t = Thread(target=play,args=("张三+%d"%i,))
t.start()
t2 = time.time()
print("耗时时长:%f"%(t2-t1))

image.png
- Daemon的使用
import time
from threading import Thread
def play(name):
print(f"hello,{name} to play")
time.sleep(1)
print("play end")
t1 = time.time()
if __name__=='__main__':
for i in range(5):
t = Thread(target=play,args=("张三+%d"%i,))
t.setDaemon(True)
t.start()
print("我是主线程")
t2 = time.time()
print("耗时时长:%f"%(t2-t1))

image.png
- join
import time
from threading import Thread
def play(name):
print(f"hello,{name} to play")
time.sleep(1)
print("play end")
t1 = time.time()
if __name__=='__main__':
for i in range(5):
t = Thread(target=play,args=("张三+%d"%i,))
t.setDaemon(True)
t.start()
t.join()
print("我是主线程")
t2 = time.time()
print("耗时时长:%f"%(t2-t1))

image.png
三、线程的封装
**1、示例
import time
from threading import Thread
class MyThread(Thread):
def run(self):
print("comein")
time.sleep(2)
print("123")
if __name__=='__main__':
print("comein 主线程")
t = MyThread()
t.start()
print("主线程结束")

image.png
2、start() vs run()
- start:start 方法是生命分到一个子线程的函数已经就绪,等待被CPU执行
- run: run方法是执行到这个子线程时,自动调用的方法
import time
from threading import Thread
import threading
class MyThread(Thread):
def run(self):
print("当前线程执行的名称时:",threading.current_thread().name)
if __name__=='__main__':
print("comein 主线程")
t = MyThread()
t.run()
print("主线程结束")

image.png

image.png
**3、线程的无序
同样的代码,多次运行,结果不一样
import time
from threading import Thread
import threading
def task():
time.sleep(2)
print("当前执行的线程是:",threading.current_thread().name)
if __name__=='__main__':
for i in range(5):
t = Thread(target=task)
t.start()

image.png

image.png
四、多线程的资源共享
import time
from threading import Thread
lst = []
def write_data():
for i in range(10):
lst.append(i)
time.sleep(0.3)
print(f"写入的数据是:{lst}")
def read_data():
print("我读取到的数据是:",lst)
if __name__=='__main__':
t = Thread(target=write_data)
t1 = Thread(target=read_data)
t.start()
t1.start()
print("主线程end")
print(lst)

image.png
这里资源共享的问题会造成:数据还没写完,就被读取完了,
可以用join来解决这个问题,等写完了后再执行后面的函数
import time
from threading import Thread
lst = []
def write_data():
for i in range(10):
lst.append(i)
time.sleep(0.3)
print(f"写入的数据是:{lst}")
def read_data():
print("我读取到的数据是:",lst)
if __name__=='__main__':
t = Thread(target=write_data)
t1 = Thread(target=read_data)
t.start()
t.join()
t1.start()
print("主线程end")
print(lst)

image.png
五、多线程的资源竞争
**1、示例:
from threading import Thread
a = 0
b = 1000000
def sum1():
global a
for i in range(b):
a += 1
print("第一次计算a:",a)
#sum1()
def sum2():
global a
for i in range(b):
a += 1
print("第二次计算a:",a)
#sum2()
if __name__ == '__main__':
t1 = Thread(target=sum1)
t2 = Thread(target=sum2)
t1.start()
t2.start()

image.png
2、资源竞争的解决方案
-
线程等待 join (同步):
同步:先后顺序执行,例如:你先说完,我再说。

image.png
-
互斥锁:
能够保证多个线程访问共享数据不会出现数据错误问题。对共享书进行锁定,保证同一时刻只有一个线程操作。
1)互斥锁的使用:
加锁acquire()
释放锁release()
acquire和release总是成对出现
2)示例:
from threading import Thread,Lock
a = 0
b = 1000000
lock = Lock()
def sum1():
lock.acquire()
global a
for i in range(b):
a += 1
print("第一次计算a:",a)
lock.release()
#sum1()
def sum2():
lock.acquire()
global a
for i in range(b):
a += 1
print("第二次计算a:",a)
lock.release()
#sum2()
if __name__ == '__main__':
t1 = Thread(target=sum1)
t2 = Thread(target=sum2)
t1.start()
t2.start()

image.png
