一 .线程的创建(Threading.Thread)
1.threading模块
Python提供了几个用于多线程编程的模块,包括thread、threading和Queue等。thread和threading模块允许程序员创建和管理线程。thread模块提供了基本的线程和锁的支持,
threading提供了更高级别、功能更强的线程管理的功能。Queue模块允许用户创建一个可以用于多个线程之间共享数据的队列数据结构。
避免使用thread模块,因为更高级别的threading模块更为先进,对线程的支持更为完善,而且使用thread模块里的属性有可能会与threading出现冲突;
其次低级别的thread模块的同步原语很少(实际上只有一个),而threading模块则有很多;再者,thread模块中当主线程结束时,所有的线程都会被强制结束掉,
没有警告也不会有正常的清除工作,至少threading模块能确保重要的子线程退出后进程才退出。
thread模块不支持守护线程,当主线程退出时,所有的子线程不论它们是否还在工作,都会被强行退出。而threading模块支持守护线程,守护线程一般是
一个等待客户请求的服务器,如果没有客户提出请求它就在那等着,如果设定一个线程为守护线程,就表示这个线程是不重要的,在进程退出的时候,不用等待这个线程退出。
multiprocess模块的完全模仿了threading模块的接口,二者在使用层面,有很大的相似性,因而不再详细介绍(官方链接)
2.线程的创建
from threading import Thread import time def sayhi(name): time.sleep(2) print('%s say hello' %name) if __name__ == '__main__': t=Thread(target=sayhi,args=('egon',)) t.start() print('主线程') 执行: 主线程 egon say hello
import threading,time def run(): print("子线程(%s)开始"%(threading.current_thread().name)) # 实现线程功能 print("打印了") time.sleep(2) print("子线程(%s)结束"%(threading.current_thread().name)) if __name__ == '__main__': # 任何进程默认就会启动一个线程 称为主线程 主线程可以器动新的子线程 # current_thread() #返回当前线程的实例 在哪个线程里面就返回哪个线程的实例 print("主线程(%s)启动"%(threading.current_thread().name)) # 打印当前线程名称 # 01创建子线程 # target=run执行指定代码 # name 是指线程名称 t=threading.Thread(target=run,name="rooTHread") # 线程执行 t.start() print("主线程(%s)结束"%(threading.current_thread().name)) # 打印当前线程名称 执行: 主线程(MainThread)启动 子线程(rooTHread)开始 主线程(MainThread)结束 打印了 子线程(rooTHread)结束
import threading,time aa=100000000000000000000000 def run(): print("子线程(%s)开始"%(threading.current_thread().name)) # 实现线程功能 print("打印了") time.sleep(2) print(aa) print("子线程(%s)结束"%(threading.current_thread().name)) if __name__ == '__main__': print("主线程(%s)启动"%(threading.current_thread().name)) # 打印当前线程名称 t=threading.Thread(target=run,name="rooTHread") # 线程执行 t.start() # 等待线程结束 t.join() print("主线程(%s)结束"%(threading.current_thread().name)) # 打印当前线程名称 # 主线程(MainThread)启动 # 子线程(rooTHread)开始 # 打印了 # 100000000000000000000000 # 子线程(rooTHread)结束 # 主线程(MainThread)结束
from threading import Thread def run(n): print(11111) print(n) p=Thread(target=run,args=("子线程",)) print("主线程!!!!!") p.start()
使用类来启动多线程
class Mythread(Thread): def __init__(self,aa): super().__init__() # 继承线程 self.aa=aa def run(self): print("111111111") print(self.aa) def run1(self): print("哈哈哈哈哈哈") p=Mythread("子线程") p.start() p.run1()
3. 多线程与多进程
启动多线程
from threading import Thread def run(n,i): print(n,"个数",i) for i in range(20): print("主线程!!!!!") p=Thread(target=run,args=("子线程",i)) p.start()
启动多线程和多进程
import time,os from multiprocessing import Process from threading import Thread def work(): print('hello') if __name__ == '__main__': # 在主进程下开启线程 t = Thread(target=work) t.start() print('主线程/主进程')
''' 打印结果: hello 主线程/主进程 ''' # 在主进程下开启子进程 t = Process(target=work) t.start() print('主线程/主进程')
''' 打印结果: 主线程/主进程 hello '''
线程和进程pid的比较
import time,os
from multiprocessing import Process
from threading import Thread
def work():
print('hello',os.getpid())
if __name__ == '__main__':
#part1:在主进程下开启多个线程,每个线程都跟主进程的pid一样
t1=Thread(target=work)
t2=Thread(target=work)
t1.start()
t2.start()
print('主线程/主进程pid',os.getpid(),"22222222222222222222222222222222222222")
# 执行结果
hello22340
hello22340
主线程 / 主进程pid2234022222222222222222222222222222222222222
print("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@2")
#part2:开多个进程,每个进程都有不同的pid
p1=Process(target=work)
p2=Process(target=work)
p1.start()
p2.start()
print('主线程/主进程pid',os.getpid(),"111111111111111111111111111111111111111")
执行结果
主线程/主进程pid 22340 111111111111111111111111111111111111111
hello 20204
hello 14772
开启多个进程和多个线程时间比较
import time,os from multiprocessing import Process from threading import Thread def fun2(n): print("进程/线程-->%s"%n,os.getpid()) if __name__=="__main__": # 进程 star=time.time() print("主进程", os.getpid()) # part1:在主进程下开启多个线程,每个线程都跟主进程的pid一样 for i in range(50): p=Process(target=fun2,args=(i,)) p.start() p.join() t2=time.time()-star print("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@") # 线程 print("主线程", os.getpid()) # part2:开多个进程,每个进程都有不同的pid st= time.time() for i in range(50): aa= Thread(target=fun2,args=(i,)) aa.start() aa.join() t3=time.time()-st print("进程执行时间为%s----线程执行时间为%s"%(t2,t3)) # 进程执行时间为5.123770236968994----线程执行时间为0.015601158142089844
4.线程数据共享(全局变量)
注意多线程内部有自己的数据栈 数不共享
注意: 多线程和多进程最大的不同在于 多进程中
同一个变量 各自有一份拷贝存在每个进程中
互相不影响 而多线程 所有变量都有所有线程共享 所以任何一个变量都可以被任意
一个线程修改 因此 线程之间共享数据最大的危险在于多个线程同时修改一个变量
容易吧内容改乱
import time,os from multiprocessing import Process from threading import Thread aa=66666 def work(n): global aa aa=10 aa=n+aa print('hello',os.getpid()) # hello 18892
#print('hello',os.getpid(),id(aa)) # hello 18892 2006806592
if __name__ == '__main__': print(aa) # 66666 这里还是打印的全局aa t1=Thread(target=work,args=(10,)) t1.start() print(aa) # 20 print(aa) # 20
# print(id(aa)) 2006806592
print(aa) # 20
因此 线程之间共享数据最大的危险在于多个线程同时修改一个变量 容易吧内容改乱
import threading,time aa=0 def run(n): for i in range(1000000): global aa aa=aa+n #15=9+5 aa=aa-n #9 if __name__ == '__main__': t1=threading.Thread(target=run,args=(6,)) t2=threading.Thread(target=run,args=(9,)) t1.start() t2.start() t1.join() t2.join() print("aa:",aa) # 线程1 aa=aa+6 # aa-6=3 # 线程2 aa=aa+9 # 3-9=-6
解决线程数据共享紊乱 枷锁
""" 两个线程同时工作 一个存钱 一个取钱 可能导致数据异常 思路:加锁 能解决数据乱的问题 """ import threading, time # 锁对象 lock = threading.Lock() aa = 0 def run(n): for i in range(5): global aa # 加锁 # 确保了这段代码只能由一个线程从头到尾的完整性 从而保持了数据不会乱 # 加锁因为阻止了多线程的并发 包含锁的某段代码实际上只能以单线程模式执行 # 从而效率降低了 要比单线程快点 # 由于他可以存在多个锁 不同线程持有不同的锁 有可能试图获取其它的锁 # 可能造成死锁 导致多个线程挂起 只能靠操作系统了强制终止 lock.acquire() try: aa = aa + n # 15=9+5 aa = aa - n # 9 finally: # 修改完一定要释放锁 lock.release() if __name__ == '__main__': t1 = threading.Thread(target=run, args=(6,)) t2 = threading.Thread(target=run, args=(9,)) t1.start() t2.start() print("aa:", aa) t1.join() t2.join() print("aa:", aa) """# 与上面的代码功能相同 with look 可以自动上锁 解锁 # 只是这样可以降低死锁的概率 with lock: aa=aa+n aa=aa-n """
多线程局部变量之threading.local()用法
假如,开了十个线程并且做同样的一件事,他们需要带着自己的数据进来,完成事情后带着自己的数据出去。如果是并发,同时进来,他们的数据就会混乱。
一般情况,我们加锁就可以了,一个人先进来,先加锁,另一个人过来看到加锁了,就在外面等,等里面的人出来,自己进去加锁,这样就不会出现数据混乱的问题。
另一种解决方法就是threading.local()来解决问题。
import threading num=0 """创建一个全局的thraedlocal 每个线程有独有的存储空间 每个线程threading对象都可以读写 但是互不影响""" local=threading.local() def run(x,n): # 每个线程都有local.x 就是线程的局部变量 x=x+n print(id(x),x,1111111111111111111111111111111111111111111111111111111) x=x-n print(id(x),x,22222222222222222222222222222222222222222222222222222) def func(n): local.x=num run(local.x,n) print(threading.current_thread().name,local.x) if __name__ == '__main__': t1=threading.Thread(target=func,args=(6,)) t1.start() t1.join() print("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@") t2 = threading.Thread(target=func, args=(9,)) t2.start() t2.join() print("aa:",num) 2006806144 6 1111111111111111111111111111111111111111111111111111111 2006805952 0 22222222222222222222222222222222222222222222222222222 Thread-1 0 @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 2006806240 9 1111111111111111111111111111111111111111111111111111111 2006805952 0 22222222222222222222222222222222222222222222222222222 Thread-2 0 aa: 0
from threading import Thread import time from threading import Thread ret = -1 # 先定义一个变量 def task(arg): # 写个任务,加个参数 global ret # 声明ret全局变量 ret = arg # 每一个线程进来到要改这个变量 print(ret) # 每个线程来,改了ret,然后取ret的值 for i in range(10): # i是线程的值,0 1 2 3 4 5 6 7 8 9 t = Thread(target=task, args=(i,)) # 开10个线程 t.start() t.join() print("主线程",ret) #9 print("****************************************88") 这个程序开了10个线程,每个线程都执行了更改ret的值并获取ret更改后的值,如果非常快,他们取到的值都不一样. ret =1 # 先定义一个变量 def task(arg): # 写个任务,加个参数 global ret # 声明ret全局变量 ret = arg # 每一个线程进来到要改这个变量 time.sleep(2) print(ret) # 每个线程来,改了ret,然后取ret的值 for i in range(10): # i是线程的值,0 1 2 3 4 5 6 7 8 9 t = Thread(target=task, args=(i,)) # 开10个线程 t.start() # 打印结果 9 9 9 9 9 9 9 9 9 9 print("****************************************88") from threading import Thread from threading import local import time # 这是一个特殊的对象 ret = local() # 先实例化一个对象 def task(arg): # 写个任务,加个参数 ret = arg # 每一个线程进来都给他开辟一个独立的空间 单纯的threading.local()的作用就是这个 time.sleep(2) print(ret) # 每个线程来,改了ret,然后取ret的值 for i in range(10): # i是线程的值,0 1 2 3 4 5 6 7 8 9 t = Thread(target=task, args=(i,)) # 开10个线程 t.start() # 打印结果 0 3 2 5 7 9 8 4 1 6 threading.local()的作用就是为每个线程开辟一个独立的空间进行数据存储。
5.线程其他方法
Thread实例对象的方法 # isAlive(): 返回线程是否活动的。 # getName(): 返回线程名。 # setName(): 设置线程名。 threading模块提供的一些方法: # threading.currentThread(): 返回当前的线程变量。 # threading.enumerate(): 返回一个包含正在运行的线程的list。正在运行指线程启动后、结束前,不包括启动前和终止后的线程。 # threading.activeCount(): 返回正在运行的线程数量,与len(threading.enumerate())有相同的结果。
from threading import Thread,current_thread,enumerate,activeCount def work(): print('%s is running'%current_thread().getName()) if __name__ == '__main__': t=Thread(target=work) # 线程 t.start() # t.join() print(t.is_alive()) # 返回线程是否活动的。 print(t.getName()) # 返回线程名 print(current_thread().getName()) # 当前线程名 print(enumerate()) # 返回一个包含正在运行的线程的list。正在运行指线程启动后、结束前,不包括启动前和终止后的线程。 print(activeCount()) # 返回正在运行的线程数量 print('主线程') """Thread-1 is running False Thread-1 MainThread [<_MainThread(MainThread, started 12956)>] 1 主线程 """
6. 线程守护
守护线程 无论是进程还是线程,都遵循:守护xx会等待主xx运行完毕后被销毁。需要强调的是:运行完毕并非终止运行 #1.对主进程来说,运行完毕指的是主进程代码运行完毕 #2.对主线程来说,运行完毕指的是主线程所在的进程内所有非守护线程统统运行完毕,主线程才算运行完毕
#1 主进程在其代码结束后就已经算运行完毕了(守护进程在此时就被回收),然后主进程会一直等非守护的子进程都运行完毕后回收子进程的资源(否则会产生僵尸进程),才会结束, #2 主线程在其他非守护线程运行完毕后才算运行完毕(守护线程在此时就被回收)。因为主线程的结束意味着进程的结束,进程整体的资源都将被回收,而进程必须保证非守护线程都运
行完毕后才能结束。
import threading,os,time def func1(n): while True: print(n,"----子线程一",os.getpid()) time.sleep(0.5) def func2(n): print(n,"-----子线程2",os.getpid()) time.sleep(1) if __name__ == '__main__': t1=threading.Thread(target=func1,args=(6,)) t1.daemon=True t1.start() t2=threading.Thread(target=func2,args=(9,)) t2.start() print("主线程!!!!!!!!", os.getpid())
6 ----子线程一 17140
9 -----子线程2 17140
主线程!!!!!!!! 17140
6 ----子线程一 17140
from threading import Thread import time def foo(): print(123) time.sleep(1) print("end123") def bar(): print(456) time.sleep(3) print("end456") t1=Thread(target=foo) t2=Thread(target=bar) t1.daemon=True t1.start() t2.start() print("main-------") # 123 # 456 # main------- # end123 # end456
from multiprocessing import Process import os,time def func1(n): while True: print(n, "----子线程一", os.getpid()) time.sleep(0.5) def func2(n): print(n,"-----子线程2",os.getpid()) if __name__ == '__main__': print("主进程!!!!!!!!",os.getpid()) t1=Process(target=func1,args=(6,)) t1.daemon=True t1.start() t2=Process(target=func2,args=(9,)) t2.start() # t1.daemon=True 守护进程 # :守护进程会在主进程代码执行结束后就终止 # 进程之间是互相独立的,主进程代码运行结束,守护进程随即终止