Python ---- 多线程的详细解读
多线程
主线程 :主线程启动会有一个默认的主线,通常称为主线。
线程 可简单的理解为是程序执行的一条分支,也是程序执行流的最小单位。它是被系统独立调度和分派的基本单位。
主线程的作用:1)创建子线程 。
2)等其他子线程执行结束后,做关闭操作。
1.导入模块 threading
2.创建线程对象 threading.Tread(target=执行的分支函数名)
3.启动子线程。子线程对象.start()
'''
1、导入模块 threading 模块
2、创建线程类的对象
3、启动线程
'''
import threading
import time
def dance(name1):
for i in range(5):
print("%s在跳舞"%(name1))
time.sleep(1)
def sing(name2):
for i in range(5):
print("%s在唱歌"%(name2))
time.sleep(2)
Mytread1 = threading.Thread(target=dance,args=("小红",))
Mytread2 = threading.Thread(target=sing,args=("小明",))
Mytread1.start()
Mytread2.start()
threading.enumerate 查看当前活跃线程对象
len( threading.enumerate) 查看线程数量
线程的名称 threading.current_thread().name
当前线程的对象 threading.current_thread() ,对象里有名称
import threading
import time
def dance(name1):
for i in range(5):
print("%s在跳舞"%(name1))
time.sleep(1)
print("当前的线程1",threading.current_thread().name)
def sing(name2):
for i in range(5):
print("%s在唱歌"%(name2))
time.sleep(2)
print("当前线程2",threading.current_thread().name)
Mytread1 = threading.Thread(target=dance,args=("小红",))
Mytread2 = threading.Thread(target=sing,args=("小明",))
print("当前所有线程3",threading.enumerate())
Mytread1.start()
print("当前所有线程4",threading.enumerate())
Mytread2.start()
print("当前所有线程5",threading.enumerate())
print("当前所有线程数",len(threading.enumerate()))
在线程中有三种传递方法
1、使用元祖传递threading.Tread(target = 线程函数名称, args = (参数1,参数2…))
2、使用字典传递 Tread(target = 线程函数名称, kwargs = (‘a’:10,‘b’ = 20…)
3、使用元祖 字典传值 threading.Tread(target = 线程函数名称, args = (参数1,参数2…),kwargs = {‘a’:10,‘b’ = 20…)}**
线程执行顺序是无序的,由cpu 调度的,根据运行状态来调度 ,无法控制 。
import threading
def my_tread(a,b,c):
print("参数:",a,b,c)
Mytread1 = threading.Thread(target=my_tread,args=(1,2,3)) #参数args 以元祖的形式
Mytread2 = threading.Thread(target= my_tread,kwargs={'a':10,'b':20,'c':30}) #参数 kwargs 以字典的形式
Mytread3 = threading.Thread(target= my_tread,args=(10,),kwargs={'b':20,'c':30}) #参数args, kwargs 以元祖、字典的混合形式
Mytread1.start()
Mytread2.start()
Mytread3.start()
守护线程设置方式为子线程.setDaemon(True), 这种是主线程结束后,子线程会跟着结束
exit() 程序主动退出,主线程主动结束;(测试会用到)
子线程守护主线程,就是说子线程和主线程的一种约定,主线程结束后子线程会跟着结束。
import threading
import time
def My_Tread():
for i in range(10):
print("我是子进程")
time.sleep(1)
if __name__ == '__main__':
print("主进程开始")
Mytread = threading.Thread(target=My_Tread)
Mytread.setDaemon(True)
print("子进程开始运行")
Mytread.start()
time.sleep(5)
print("主进程结束")
exit()
创建自定义线程
1、自定义类继承 threading.thread 类 如:class MyTread(treading.tread)
2、重写父类(threading.thread) run方法: def run(self)
3、通过创建子类对象,让子类对象.start() 就可以启动子线程,这里的 start() 方法 里面有 run()方法
Mythread = MyTread()
Mythread.start()
self.name 是从父类 继承 thread 类继承过来的
子类要调用 init 方法 先要重写父类方法的 super().init ()方法
底层原理: Treadl 类
-
run 方法
-
start 方法
-
start() 方法调用了run方法
-
自定义中 __init__方法,子类要先通过super调用父类的初始化方法,子类再初始化
import threading
import time
class MyTread(threading.Thread):
#类有参数的话,得初始化
def __init__(self,name):
# 因为MyTread类是继承父类,父类中也有init()方法,所以的重写父类的方法
super().__init__()
self.name = name
#重写run()方法,PS:重写的意思是将写子类与父类的同名方法
def run(self):
for i in range(5):
print("子线程程1:%s"%(self.name))
time.sleep(1)
mytread = MyTread("小红")
mytread.start()
多线程之间可以共享全局变量
import threading
import time
num = 0
def run1():
global num
for i in range(10):
num +=1
print("线程1",num)
def run2():
print("线程2",num)
if __name__ == '__main__':
T1 = threading.Thread(target=run1)
T2 = threading.Thread(target=run2)
T1.start()
T2.start()
while len(threading.enumerate()) != 1:
time.sleep(1)
print("主线程",num)
存在的问题:共享全局变量会导致资源竞争问题,当运算的数字足够大时会造成资源的竞争,影响数据的准确性。
解决方法:优先让某个线程先执行
线程对象.join
缺点:会把多线程变成单线程,影响整体性能。
import threading
import time
num = 0
def run1():
global num
for i in range(100000):
num +=1
print("线程1",num)
def run2():
global num
for i in range(100000):
num += 1
print("线程2",num)
if __name__ == '__main__':
T1 = threading.Thread(target=run1)
T2 = threading.Thread(target=run2)
T1.start()
T1.join() # 等待子线程T1运行结束后,主线程才向下运行
T2.start()
while len(threading.enumerate()) != 1:
time.sleep(1)
print("主线程",num)
-
同步和异步
- 同步:多任务,多个任务之间的执行的时候要求有先后顺序,必须一个执行完了之后,另一个才能执行,只有一个主线。
- 异步:指的是,多个任务之间没有执行的顺序,可以同时执行,执行的先后顺序没有影响,存在多条主线。
线程锁的机制:当一个线程获得一共享资源后,立即进行锁住,任务执行完成后再解锁,有效的保证同一时间只有一个线程在使用资源。
-
同步互斥锁
多个线程几乎同时修改一个共享数据的时候,需要进行同步控制。
线程同步能够保证多个线程安全访问竞争资源,最简单的同步机制是引入互斥锁。
互斥锁为资源引入一个状态:锁定/非锁定
某个线程要改变共享资源时,先将其锁定,此时的资源的状态为“锁定”,其他线程不能更改;直到这个线程执行结束,释放资源,将资源的状态变成“非锁定”,其他线程才能再次锁定该资源。互斥锁保证了一次只能有一个线程进行写入操作,从而保证了多线程的情况下数据的正确性。
-
创建互斥锁
1、创建一把互斥锁: lock1 = threading.Lock()
2、上锁 lock1.acqurie()
3、解锁 lock1.release()
互斥锁的使用原则:尽量少的锁定竞争资源
import threading import time #创建一包锁 lock1 = threading.Lock() num = 0 def run1(): global num for i in range(100000): #上锁 lock1.acquire() num +=1 #解锁 lock1.release() print("线程1",num) def run2(): global num for i in range(100000): #上锁 lock1.acquire() num += 1 #解锁 lock1.release() print("线程2",num) if __name__ == '__main__': T1 = threading.Thread(target=run1) T2 = threading.Thread(target=run2) T1.start() T2.start() while len(threading.enumerate()) != 1: time.sleep(1) print("主线程",num)
-
-
死锁
在线程*享多个资源的时候,如果两个线程分别占有一部分资源并且同时等待对方释放资源,就造成死锁。
import threading def work1(index): a = [1, 3, 5, 7, 9] #上锁 lock1.acquire() if index >= len(a): print("%d 下标越界"%(index)) return print(a[index]) #解锁 lock1.release() if __name__ == '__main__': lock1 = threading.Lock() for i in range(10): mythread = threading.Thread(target=work1,args=(i,)) mythread.start() ''' 运行结果 1 3 5 7 9 5 下标越界 ''' 卡住了,下面几个线程都在等着释放资源,在return 前释放资源
上一篇: 简单的蓝牙
下一篇: Nodejs MSSQL详细解读