多线程
1.主线程:创建一个进程的时候,会创造一个线程,这个线程就被成为主线程
一个进程里只有一个主线程
注意:python中的多线程不是真正意义上的多线程。(高速的在多个线程中进行切换,制造出多线程的假象)
2.全局锁(GIL): 在任意的指定时间里,有且只有一个线程在运行(保证python线程安全)
3. Join:等待上面的所有线程结束之后在继续往下执行
线程安全的歧义:
4.多线程复杂度较高,不建议使用(使用的话使用消息传递,写脚本的话可以使用,不用考虑脚本的阅读性):
一个程序的复杂度,和代码长度有关
用于数据库连接池
5.io操作多线程,必须要lock,acquire,release,Rlock
互斥锁:多线程对同一个文件进行操作时,当一个线程在操作时候,其它线程不能进行操作,直到该线程结束后,才能进行操作
加锁:acquire
释放锁:release
死锁:加锁后不释放
防止死锁:Rlock(可重入),在加锁中在加锁,(几个加锁要有几个解锁)
守护线程: 必须在start() 方法调用之前设置,如果不设置为守护线程程序会被无限挂起。子线程启动后,父线程也继续执行下去,当父线程执行完最后一条语句后,没有等待子线程,直接就退出了,同时子线程也一同结束。
上下文:每个线程都有他自己的一组CPU寄存器,称为线程的上下文,该上下文反映了线程上次运行该线程的CPU寄存器的状态。
指令指针和堆栈指针寄存器:是线程上下文中两个最重要的寄存器,线程总是在进程得到上下文中运行的,这些地址都用于标志拥有线程的进程地址空间中的内存。
注意:加锁后一定要释放
优点:
- 使用线程可以把占据长时间的程序中的任务放到后台去处理。
- 用户界面可以更加吸引人,这样比如用户点击了一个按钮去触发某些事件的处理,可以弹出一个进度条来显示处理的进度
- 程序的运行速度可能加快
- 在一些等待的任务实现上如用户输入、文件读写和网络收发数据等,线程就比较有用了。在这种情况下我们可以释放一些珍贵的资源如内存占用等等。
线程可以被抢占(中断)。
在其他线程正在运行时,线程可以暂时搁置(也称为睡眠) -- 这就是线程的退让。
线程分类
- 内核线程:由操作系统内核创建和撤销。
- 用户线程:不需要内核支持而在用户程序中实现的线程。
threading 模块除了包含 _thread 模块中的所有方法外,还提供的其他方法:
- threading.currentThread(): 返回当前的线程变量。
- threading.enumerate(): 返回一个包含正在运行的线程的list。正在运行指线程启动后、结束前,不包括启动前和终止后的线程。
- threading.activeCount(): 返回正在运行的线程数量,与len(threading.enumerate())有相同的结果。
线程模块同样提供了Thread类来处理线程,Thread类提供了以下方法:
- run(): 用以表示线程活动的方法。
- start():启动线程活动。
- join([time]): 等待至线程中止。这阻塞调用线程直至线程的join() 方法被调用中止-正常退出或者抛出未处理的异常-或者是可选的超时发生。
- isAlive(): 返回线程是否活动的。
- getName(): 返回线程名。
- setName(): 设置线程名。
EG1:
# threading.Thread 继承创建一个新的子类,并实例化后调用 start() 方法启动新线程,即它调用了线程的 run() 方法:
import threading, time
class My(threading.Thread):
def __init__(self, id, name, counter):
super().__init__()
self.id = id
self.name = name
self.counter = counter
def run(self):
print("开始")
display(self.id, self.name, self.counter)
print("结束")
def display(id, name, counter):
for i in range(counter):
print("id:{},name:{}".format(id, name))
time.sleep(1.0)
t1 = My(1, "t1", 5)
t2 = My(2, "t2", 5)
t1.start()
t2.start()
t1.join()
t2.join()
print("退出主线程")
实行结果:
开始
id:1,name:t1
开始
id:2,name:t2
id:1,name:t1
id:2,name:t2
id:1,name:t1
id:2,name:t2
id:2,name:t2
id:1,name:t1
id:1,name:t1
id:2,name:t2
结束
结束
退出主线程
EG2:
# 线程优先级队列( Queue)
import queue, time, threading
# 循环标志
queue_flg = 0
# 自定义线程,继续threading.Thread
class Mythreading(threading.Thread):
# 重写初始化方法,实现对象属性的赋值
def __init__(self, id, name, queue1):
threading.Thread.__init__(self)
self.name = name
self.id = id
self.queue1 = queue1
# 实例化对象调用start()方法时候,会自动调用run()方法
def run(self):
print("开始:{}".format(self.name))
display(self.id, self.name, self.queue1)
print("结束:{}".format(self.name))
# 当队列不为空的时候,打印一个队列元素,使用线程锁
def display(id, name, queue1):
while not queue_flg:
lock.acquire()
# print(name)
if not queue1.empty():
print("id:{}, name:{}, queue:{} ".format(id, name, queue1.get()))
lock.release()
else:
lock.release()
time.sleep(1)
# 创建一个线程锁对象
lock = threading.Lock()
# 创建一个做大长度为100的队列
queue1 = queue.Queue(100)
# 线程名称列表
list1 = ["thread1", "thread2", "thread3", "thread4"]
# 创建线程对象后,添加的列表
list_thread = []
id = 1
# 循环list1列表,创建对象,执行start()方法
for i in list1:
a = Mythreading(id, i, queue1)
a.start()
# 添加到list_thread列表
list_thread.append(a)
id += 1
# 创建列表,并添加队列元素
list2 = [i for i in range(5)]
lock.acquire()
for i in list2:
queue1.put(i)
lock.release()
# 当队列不为空的时候,不进行主线程操作
while not queue1.empty():
pass
# 结束循环
queue_flg = 1
#使当前多线完成后,在继续执行主线程
for i in list_thread:
i.join()
print("多线程结束")
实行结果:
开始
id:1,name:t1
id:1,name:t1
id:1,name:t1
结束
开始
id:2,name:t2
id:2,name:t2
id:2,name:t2
结束
退出主线程
开始:thread1
开始:thread2
开始:thread3
开始:thread4
id:4, name:thread4, queue:0
id:2, name:thread2, queue:1
id:3, name:thread3, queue:2
id:1, name:thread1, queue:3
id:4, name:thread4, queue:4
结束:thread2
结束:thread3
结束:thread1
结束:thread4
多线程结束