代码改变世界

Python多线程笔记记录

2020-12-30 02:40  那个杰克  阅读(117)  评论(0)    收藏  举报

顿悟,需要多学习,多写笔记,以谢罪天下!

线程的多种实现方法:
Thread模块
Treading模块
Queue模块 -- 未学习


先说一下Thread模块,Thread模块会带上锁机制GIL (global interpreter lock 全局解释锁),简单理解就是让线程,不会因为主线程的退出,导致线程直接终止运行

直接上代码

如果平时不用多线程开发,是这样子的
from time import ctime,sleep
def loop0():
    print("start loop0 at",ctime())
    sleep(4)
    print("start loop0 at",ctime())
    
def loop1():
    print("start loop1 at",ctime())
    sleep(2)
    print("start loop1 at",ctime())

def main():
    print("开始:",ctime())
    loop0()
    loop1()
    print("结束:",ctime())

运行结果:
开始: Wed Dec 30 02:32:44 2020
start loop0 at Wed Dec 30 02:32:44 2020
start loop0 at Wed Dec 30 02:32:48 2020
start loop1 at Wed Dec 30 02:32:48 2020
start loop1 at Wed Dec 30 02:32:50 2020
结束: Wed Dec 30 02:32:50 2020
loop0 和 loop1 首次输出结果相差了4秒,没有并行执行
[Finished in 6.2s]

如果线程没有锁的情况下:

无锁机制
import _thread
import time


def loop0():
	print("start loop0 at:-->",time.ctime())
	time.sleep(4)
	print("start loop0 at:-->",time.ctime())

def loop1():
	print("start loop1 at:-->",time.ctime())
	time.sleep(2)
	print("start 1 done at:-->",time.ctime())

def main():
	print("starting at:-->",time.ctime())
	_thread.start_new_thread(loop0,())
	_thread.start_new_thread(loop1,())
	#
	#time.sleep(6)
	print("all done at:-->",time.ctime())

if __name__=="__main__":
	main()
注释: 如果去掉time.sleep(6),等待主线程退出的机制。主进程退出后,正在运行的线程就直接退出了

运行结果:
未注释time.sleep(6),卡住主线程
tarting at:--> Wed Dec 30 02:19:11 2020
start loop0 at:--> Wed Dec 30 02:19:11 2020
start loop1 at:--> Wed Dec 30 02:19:11 2020
start 1 done at:--> Wed Dec 30 02:19:13 2020
start loop0 at:--> Wed Dec 30 02:19:15 2020
all done at:--> Wed Dec 30 02:19:17 2020
loop0 和 loop1 首次输出结果时间相同,并行执行了
[Finished in 6.2s] 总共运行6.2秒

注释time.sleep(6)后,直接进入主线程
starting at:--> Wed Dec 30 02:01:48 2020
all done at:--> Wed Dec 30 02:01:48 2020
[Finished in 0.2s]
全部执行完:0.2秒,线程没执行完即终止

import _thread
from time import sleep,ctime

loops=[4,2]

def loop(nloops,nsec,lock):
	print("start loop","->",nloops,"《标志位》","at:",ctime())
	sleep(nsec)
	print("loop",nloops,"《标志位》","->",'doop at:',ctime())
	lock.release() #3.释放锁

def main():
	
	print("starting at:",ctime())
	#生产几把锁,放到队列里
	locks=[]
	#生成索引
	nloops=range(len(loops))

	#创建锁
	for i in nloops:
		lock = _thread.allocate_lock() #创建一把锁 -- 1.创建锁
		lock.acquire() #加上这句,给锁自动对对象上锁 --2.获得锁
		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(): #判断锁的状态,如果处于被锁状态,一次处于等待,拖住主线程 --4.检查锁
			pass

	print('all DONE at:',ctime())

if __name__=="__main__":
	main()

不需要加time.sleep(),卡住主进程,主进程也会等待线程运行完成,且解锁后,才停止

运行效果:
starting at: Wed Dec 30 02:03:17 2020
start loop 0 ->《标志位》 at:0 Wed Dec 30 02:03:17 2020
start loop 1 ->《标志位》
at: Wed Dec 30 02:03:17 2020
loop 1 《标志位》 -> doop at: Wed Dec 30 02:03:19 2020
loop 0 《标志位》 -> doop at: Wed Dec 30 02:03:21 2020
all DONE at: Wed Dec 30 02:03:21 2020
[Finished in 4.5s] 执行4.5秒

*一般已经放弃使用Thread创建线程,因为Thread创建的线程,如果一旦主进程异常终止或者认为终止,所有状态的线程都会终止


使用Threading模块:
使用Threading模块有三种方式,看主要喜欢的方式,官方推荐使用第三种

  • 创建一个Thread的实例,传给它一个函数,t=threading.Thread() t(target=xxx,args=xxx)
  • 创建一个Thread的实例,传给它一个可调用的类对象
  • 从Thread派生出一个子类,创建一个这个子类的实例

使用的好处:
1.不需要创建各种状态的锁机制(分配锁,获得锁,释放锁,检查锁)
2.主线程需要等待守护线程结束后才终止

方式一:

import threading
from time import sleep,ctime

loops=[4,2]

def loop(nloop,nsec):
	print("start loop",nloop,'at:',ctime())
	sleep(nsec)
	print("loop",nloop,"done at:",ctime())


def main():
	print("starging 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: #start threads
		threads[i].start()

	for i in nloops:
		pass=
		#threads[i].join()

	print("all DONE at:",ctime())

if __name__=="__main__":
	main()

运行结果:
starging at: Wed Dec 30 02:17:24 2020
start loop 0 at: Wed Dec 30 02:17:24 2020
start loop 1 at: Wed Dec 30 02:17:24 2020
loop 1 done at: Wed Dec 30 02:17:26 2020
loop 0 done at: Wed Dec 30 02:17:28 2020
all DONE at: Wed Dec 30 02:17:28 2020
[Finished in 4.2s] 执行完:4.2秒

方式三:

import threading
from time import ctime,sleep


loops = [4,2]
class MyThread(threading.Thread):
	def __init__(self,func,args,name=''):
		#创建子类构建器时,必须要先调用基类的构造器
		threading.Thread.__init__(self)
		self.name=name
		self.func=func
		self.args=args

	def run(self):
		self.func(*self.args)

def loop(nloop,nsec):
	print("start loop",nloop,"at:",ctime())
	sleep(nsec)
	print("loop",nloop,"done at:",ctime())

def main():
	print("starting 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()

	print("all Done at:",ctime())

if __name__=="__main__":
	main()

运行结果:

starting at: Wed Dec 30 02:21:22 2020
start loop 0 at: Wed Dec 30 02:21:22 2020
start loop 1 at: Wed Dec 30 02:21:22 2020
loop 1 done at: Wed Dec 30 02:21:24 2020
loop 0 done at: Wed Dec 30 02:21:26 2020
all Done at: Wed Dec 30 02:21:26 2020
[Finished in 4.2s] 总共运行4.2秒

学习点:

  • join() 线程挂起 --主线程需要等所有挂起的线程结束后,才开始继续执行

  • isAlive() 查看线程是否处于运行状态

  • start() 开始线程的执行

  • setDaemon(True) 设置成不是守护线程,表示线程不重要,类似于,设置这个标签,表示你不是vip,主线程可以随时轰你走(主线程终止,即线程终止)

使用join挂起线程的应用场景:
希望线程运行结束后,主线程才执行,比如:等待客户输入完成

不使用join挂起线程的应用场景:
希望主线程运行的同时,线程也在同步运行,比如:???