一.理论知识
What is 进程
- 进程是执行中的程序
- 拥有独立地址空间、内存、数据栈等操作系统管理
- 派生(fork或spawn)新进程
- 进程问通信(IPC)方式共享信息
什么是线程
- 同进程下执行,并共享相同的上下文。
- 线程间的信息共享和通信更加容易
- 多线程并发执行
So,Python与线程是什么关系
- 解释器主循环
- 主循环中只有一个控制线程在执行
- 使用全局解释器锁(GIL)
GIL!?
GIL保证一个线程
- 设置GIL
- 执行下面操作之一
- 指定数量的字节码指令
- 线程主动让出控制权
- 心切换进一个线程去运行
- 把线程设置回睡眠状态(切换出线程)
- 解锁GIL
- 重复上面的步骤
二.实战
python的两种线程管理
- _thread:提供了基本的线程和锁
- threading:提供了更高级别,功能更全的线程管理(**推荐使用,基于_thread)
- 支持同步机制
- 支持守护线程
实战笔记
- 1._thread的使用
from time import sleep, ctime
import logging
import _thread
'''
@author: Luy
@data:2020-10-12 13:15
@Summary:_thread的使用与理解
'''
#设置日志级别。
logging.basicConfig(level=logging.INFO)
def loop0():
logging.info("start loop0 at " + ctime())
sleep(4)
logging.info("end loop0 at " + ctime())
def loop1():
logging.info("start loop1 at " + ctime())
sleep(2)
logging.info("end loop1 at " + ctime())
def main():
logging.info("start all at " + ctime())
_thread.start_new_thread(loop0, ())
_thread.start_new_thread(loop1, ())
#这里加sleep(6)的原因
#_thread当主线程退出之后,所有的子线程都会被kill掉(没有守护线程的概念)
sleep(6)
logging.info("end all at " + ctime())
if __name__ == '__main__':
main()
*_thread不守护线程,当主线程退出之后,所有的子线程都会被kill掉,所以需要加sleep来进行等待,确保子进程顺利执行
- 2.如何使用_thread加锁
- 在1中,两个子线程只能通过等待来确保执行,这样效率太低了,
- 在thread中,可以通过加锁来监视两个子线程是不是执行完了,如果执行完了主线程再退出
'''
@author: Luy
@data:2020-10-12 13:15
@Summary:_thread的锁操作
'''
from time import sleep, ctime
import logging
import _thread
logging.basicConfig(level=logging.INFO)
loops = [2, 4]
'''
nloop-用于标识当前loop属于第几个
nsec-时间,告诉我们这个循环了多长的时间
lock-传进来的锁,首先,会传进来一个锁住的锁,当我们loop执行结束后子线程都会进行解锁,当判断到所有的线程都解锁了,此时主进程就可以结束了
'''
def loop(nloop, nsec, lock):
logging.info("start loops" + str(nloop) + " at " + ctime())
#传一个动态时间
sleep(nsec)
logging.info("end loops " + str(nloop) + "at " + ctime())
#对锁进行一个释放
lock.release()
def main():
logging.info("start all at " + ctime())
#声明一个锁的list
locks = []
nloops = range(len(loops))
for i in nloops:
#取出之后我们声明一个新的锁lock
lock = _thread.allocate_lock()
#上锁
lock.acquire()
#加锁之后传递给locks
locks.append(lock)
# 执行子线程和加锁操作分开写的原因,获取锁的操作是需要时间的,很有可能会出现,当获取锁的时候,开启一个新的子线程,而我在获取第二个锁的时候子线程已经执行完毕了,然后出现一个解锁的操作,主线程就退出了
# 当执行第一循环执行获取一个锁的时候,执行子程序,当第二个循环获取第二个锁的时候慢于第一个子线程的执行速度,那第一个就回去进行一个解锁操作,程序就停下来了
#起线程用的循环
for i in nloops:
#起一个子线程,每开启一个
_thread.start_new_thread(loop, (i, loops[i], locks[i]))
#主线程。依次取出两个线程,然后判断锁是不是解锁的,如果解锁了,就结束线程
for i in nloops:
while locks[i].locked(): pass
logging.info("end all at " + ctime())
if __name__ == '__main__':
main()
使用threading实现上面的内容
from time import sleep, ctime
import logging
import _thread
import threading
logging.basicConfig(level=logging.INFO)
loops = [2, 4]
def loop(nloop, nsec):
logging.info("start loops" + str(nloop) + " at " + ctime())
sleep(nsec)
logging.info("end loops " + str(nloop) + "at " + ctime())
def main():
logging.info("start all at " + ctime())
threads = []
nloops = range(len(loops))
for i in nloops:
t = threading.Thread(target=loop, args=(i, loops[i]))
threads.append(t)
for i in nloops:
threads[i].start()
for i in nloops:
#等待线程是否执行完毕,如果是未完成,则堵塞在这里
threads[i].join()
logging.info("end all at " + ctime())
if __name__ == '__main__':
main()
面向对象的多线程写法
- 使用一个class继承threading,然后再调用init的方法,主动调用之后,再重写run方法,
from time import sleep, ctime
import logging
import _thread
import threading
##面向对象的写法
#原语
##锁
##信号量
##
logging.basicConfig(level=logging.INFO)
loops = [2, 4]
#创建一个类继承threading的Thread方法,
class MyThread(threading.Thread):
#1.主动调用Thread的初始构造方法,把其他传进来的参数,存到类中
def __init__(self, func, args, name=''):
threading.Thread.__init__(self)
self.func =func
self.args = args
self.name =name
#2.重写方法
def run(self):
self.func(*self.args)
def loop(nloop, nsec):
logging.info("start loops" + str(nloop) + " at " + ctime())
sleep(nsec)
logging.info("end loops " + str(nloop) + "at " + ctime())
def main():
logging.info("start all at " + ctime())
threads = []
nloops = range(len(loops))
for i in nloops:
t = MyThread(loop,(i, loops[i]), loop.__name__)
threads.append(t)
for i in nloops:
threads[i].start()
for i in nloops:
threads[i].join()
logging.info("end all at " + ctime())
if __name__ == '__main__':
main()
网友评论