python多线程threading
python多线程threading
目录
threading介绍与简单使用
threading介绍:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | threading模块 threading 模块除了包含 _thread 模块中的所有方法外,还提供的其他方法: threading.currentThread(): 返回当前的线程变量。 threading.enumerate(): 返回一个包含正在运行的线程的list。正在运行指线程启动后、结束前,不包括启动前和终止后的线程。 threading.activeCount(): 返回正在运行的线程数量,与len(threading.enumerate())有相同的结果。 除了使用方法外,线程模块同样提供了Thread类来处理线程,Thread类提供了以下方法: run(): 用以表示线程活动的方法。 start():启动线程活动。 join([time]): 等待至线程中止。这阻塞调用线程直至线程的join() 方法被调用中止-正常退出或者抛出未处理的异常-或者是可选的超时发生。 isAlive(): 返回线程是否活动的。 getName(): 返回线程名。 setName(): 设置线程名。 |
程序示例:import threading
1 | <br>import threading<br><br>def thread_job():<br><br> print ( "this is an added Thread ,number is %s" %threading.currentThread())<br><br><br>def main():<br> added_thread = threading.Thread(target=thread_job) # 定义一个新的线程,指定一个任务给target<br> added_thread.start() # 开启线程<br> print (threading.activeCount())<br> print (threading.enumerate())<br> print (threading.currentThread())<br><br><br> if __name__ == '__main__' :<br> main()<br><br> |
程序运行结果:
第一个输出是当前线程,这个是我们开启的线程
第二个输出的是在正在运行的线程的数量
第三个输出返回一个包含正在运行的线程的list,包含主线程和开启的线程
第四个输出是当前线程,最后只剩下主线程
join功能
join功能介绍:
join([time]): 等待至线程中止。这阻塞调用线程直至线程的join() 方法被调用中止-正常退出或者抛出未处理的异常-或者是可选的超时发生。
当我们有一个程序功能必须等到开启的线程执行完以后,才能运行主线程,就可以使用这个功能。
不加join的情况,这种情况下,以下例程序看来,主线程运行更快,我们看看结果
程序示例1:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | import threading import time def thread_job(): print ( "T1 start" ) for i in range(10): time.sleep(0.1) print ( "T1 finish" ) def main(): added_thread = threading.Thread(target=thread_job,name = 'T1' ) #定义一个新的线程,指定一个任务给target added_thread.start() #开启线程 print ( "all done" ) if__name__== '__main__' : main() |
程序运行结果:
加join的情况,等待我们开启的线程执行完以后才能运行主线程。
程序示例2:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | import threading import time def thread_job(): print ( "T1 start" ) for i in range(10): time.sleep(0.1) print ( "T1 finish" ) def main(): added_thread = threading.Thread(target=thread_job,name = 'T1' ) #定义一个新的线程,指定一个任务给target added_thread.start() #开启线程 added_thread.join() print ( "all done" ) if __name__ == '__main__' : main() |
程序运行结果:
queue功能
1 | 线程之间的通信 |
queue功能:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | 线程优先级队列( Queue) Python 的 Queue 模块中提供了同步的、线程安全的队列类,包括FIFO(先入先出)队列Queue,LIFO(后入先出)队列LifoQueue,和优先级队列 PriorityQueue。 这些队列都实现了锁原语,能够在多线程中直接使用,可以使用队列来实现线程间的同步。 Queue 模块中的常用方法: Queue.qsize() 返回队列的大小 Queue. empty () 如果队列为空,返回True,反之False Queue.full() 如果队列满了,返回True,反之False Queue.full 与 maxsize 大小对应 Queue.get([block[, timeout]])获取队列,timeout等待时间 Queue.get_nowait() 相当Queue.get(False) Queue.put(item) 写入队列,timeout等待时间 Queue.put_nowait(item) 相当Queue.put(item, False) Queue.task_done() 在完成一项工作之后,Queue.task_done()函数向任务已经完成的队列发送一个信号 Queue.join() 实际上意味着等到队列为空,再执行别的操作 |
先进先出模式:get与put的阻塞
#get卡 import queue #线程队列 q = queue.Queue() #线程队列的对象 q.put(12) #往队列里面放入数据 q.put("hello") q.put({"name":"yuan"}) #先进先出 #如果队列为空,取值就会被阻塞,只有当队列有值才能取到值 while 1: data = q.get() if data: print(data) print("--------") #put卡 import queue #线程队列 q = queue.Queue(3) #队列里可以存储几个值 q.put(12) #往队列里面放入数据 q.put("hello") q.put({"name":"yuan"}) q.put(1) #只能存储3个值,这里缺试图存储第四个值,只有当有别的线程取走一个值,才能存储进值 while 1: data = q.get() if data: print(data) print("--------")
在get与put阻塞的时候报错
q.put(1,False)
q.get(block=False)
先进后出模式
import queue q = queue.LifoQueue() q.put(12) q.put("hello") q.put({"name":"yuan"}) q.put(1) while 1: data = q.get() if data: print(data) print("--------")
优先级模式
import queue q = queue.PriorityQueue() q.put([3,12]) #往队列里面放入数据 q.put([2,"hello"]) q.put([1,{"name":"yuan"}]) while 1: data = q.get() if data: print(data) print("--------")
下面演示了在多线程中怎么返回线程中的运行结果,因为不能使用return返回! 使用Queue.get()和Queue.put()方法
程序示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | import threading from queue import Queue def job(l,q): for i in range(len(l)): l[i] = l[i] + 1 # 对列表里每一个值加一 q.put(l) # 把计算的结果保存到queue def main(): q = Queue() # 使用queue存放返回值 threads = [] # 创建进程列表 data = [[1, 2], [3, 4], [4, 5], [5, 6]] # 创建四个进程并启动添加到进程列表里面 for i in range(4): t = threading.Thread(target=job, args=(data[i], q)) t.start() threads.append(t) # 把所有线程都加到主线程中 for thread in threads: thread.join() # 创建存放结果的列表 results = [] # 把结果保存到results列表中 for _ in range(4): results.append(q.get()) print (results) if __name__ == '__main__' : main() |
程序运行结果:
比较多线程和不使用多线程的运行速度
我们知道python中实现多线程其实是把正在运行的线程锁住,这时候其他线程就不会运行,就是在同一时间里只有一个线程在运行,在不断地切换线程中就可以实现多线程。
下面我使用多线程和不使用线程,来做同样的运算工作,看谁运行的更快。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 | import threading from queue import Queue import time def job(l,q): for i in range(len(l)): l[i] = l[i] + 1 # 对列表里每一个值加一 q.put(l) # 把计算的结果保存到queue def normal(l): results = [] for i in range(len(l)): l[i] = l[i] + 1 results.append(l[i]) return results def main(list): q = Queue() # 使用queue存放返回值 threads = [] # 创建进程列表 # 创建四个进程并启动添加到进程列表里面 for i in range(4): t = threading.Thread(target=job, args=(list[i], q)) t.start() threads.append(t) # 把所有线程都加到主线程中 for thread in threads: thread.join() # 创建存放结果的列表 results = [] # 把结果保存到results列表中 for _ in range(4): results.append(q.get()) print (results) if __name__ == '__main__' : data1 = [[1, 2], [3, 4], [4, 5], [5, 6]] data2 = [[1, 2], [3, 4], [4, 5], [5, 6]] start1_time =time.time() main(data1) print (time.time()-start1_time) start2_time = time.time() result = [] for i in range(len(data2)): result.append(normal(data2[i])) print (result) print (time.time()-start2_time) |
运行结果:
在pycham和网上的在线编译器上都运行过,大致上来多线程运行的速度要快不少
lock锁
lock.acquire() 锁住
lock.release() 解锁
#这种方式:开启100个线程,每个线程执行的时间很短,虽然线程之间有竞争关系,但在同一时刻里只有一个线程工作,每个线程执行的速度要比cpu切换的速度要快,线程的执行之间无缝连接。 # import threading # def sub(): # global num # num -= 1 # # if __name__ == '__main__': # threading_list = [] # num = 100 # for i in range(100): # t = threading.Thread(target=sub) # t.start() # threading_list.append(t) # for i in threading_list: # t.join() # print("全部执行完成") # print("num =",num)
#这种方式:和前面不一样的是,time.sleep(0.1)的时间比cpu切换的时间慢,这样当一个线程还没有机会执行num = temp - 1,就被下一个线程拿到cpu的执行权了, import threading import time def sub(): global num temp = num time.sleep(0.1) num = temp - 1 if __name__ == '__main__': threading_list = [] num = 100 for i in range(100): t = threading.Thread(target=sub) t.start() threading_list.append(t) for i in threading_list: t.join() print("全部执行完成") print("num =",num)
#上面的问题中,因为time.sleep(0.1)的或称超过cpu切换时间,我们不需要让它切换: # import threading # import time # def sub(): # global num # lock.acquire() # temp = num # time.sleep(0.1) # num = temp - 1 # lock.release() # # if __name__ == '__main__': # threading_list = [] # num = 100 # lock = threading.Lock() # for i in range(100): # t = threading.Thread(target=sub) # t.start() # threading_list.append(t) # for i in threading_list: # t.join() # print("全部执行完成") # print("num =",num) #
同步对象
# import threading # # event = threading.Event //插件一个event对象 # # event.wait() //如果没有设置标志位,调用wait()就会被阻塞,设置了标志位就不会被阻塞 # event.set() //设置标志位 # event.clear() //清除标志位 # event.isEvent() //判断是否设置了标志位 # event用来控制线程之间的执行顺序,在一个线程改变标志位,在另一个线程就能捕捉到标志位的变化 import threading import time class Worker(threading.Thread): def run(self): event.wait() #event.set = pass,就不会继续阻塞 print("worker:哎。。。命苦") time.sleep(0.5) event.clear() #清空标志位 event.wait() #没有标志位就会被阻塞 print("worker:下班了") class Boss(threading.Thread): def run(self): print("Boss:今天加班到10点") print(event.isSet()) #False event.set() #设置标志位 time.sleep(5) print("Boss:可以下班了") print(event.isSet()) event.set() #再一次设置标志位,阻塞效果消失 if __name__ == '__main__': event = threading.Event() List = [] for i in range(5): t1 = Worker() t1.start() List.append(t1) t2 = Boss() t2.start() List.append(t2) for i in List: i.join()
信号量
信号量控制最大并发数,在一段时间内有多少线程可以运行。
import threading import time class myThread(threading.Thread): def run(self): if semaphore.acquire(): print(self.name) time.sleep(3) semaphore.release() if __name__ == '__main__': semaphore = threading.Semaphore(5) threading_list = [] num = 100 for i in range(100): t = myThread() t.start() threading_list.append(t) for i in threading_list: i.join() print("全部执行完成") print("num =",num)
解决死锁:递归锁
# import threading # import time # # # class MyThread(threading.Thread): # # def actionA(self): # A.acquire() # print(self.name,"gotA",time.ctime()) # time.sleep(2) # # B.acquire() # print(self.name,"gotB",time.ctime()) # time.sleep(1) # # B.release() # A.release() # # def actionB(self): # B.acquire() # print(self.name, "gotB", time.ctime()) # time.sleep(2) # # A.acquire() # print(self.name, "gotA", time.ctime()) # time.sleep(1) # # A.release() # B.release() # # def run(self): # self.actionA() # self.actionB() # # if __name__ == '__main__': # A = threading.Lock() # B = threading.Lock() # List = [] # for i in range(5): # t = MyThread() # t.start() # List.append(t) # for y in List: # y.join() # print("All end") # #这里只有一个锁,r_lock内部是一个计数器,内部从0开始计数,当acquire一次,计数器就会加1,release就会减1 #所以,无论何时只有一个线程拿到这个锁,用这个锁,无论再做多少次加锁、解锁的操作都是对一个锁的行为。 #内部计数大于0,别的线程就无法拿到锁!!! #执行过程:五个线程竞争,有一个线程拿到cpu执行权,这个线程执行actionA,拿到r_lock锁,计数器加一,打印一次,然后再加一次锁,计数器再加一 #线程顺序释放两把锁,这个线程是要继续往下执行actionB,r_lock锁加1,内部计数器大于0,其他线程也就拿不到这把锁了,也就保证了无论何时只有一个线程能拿到这把锁! #死锁的情况中,我们使用了两把锁,这里我们只使用了一把锁,而内部维持着多把锁。 import threading import time class MyThread(threading.Thread): def actionA(self): r_lock.acquire() print(self.name,"gotA",time.ctime()) time.sleep(2) r_lock.acquire() print(self.name,"gotB",time.ctime()) time.sleep(1) r_lock.release() r_lock.release() def actionB(self): r_lock.acquire() print(self.name, "gotB", time.ctime()) time.sleep(2) r_lock.acquire() print(self.name, "gotA", time.ctime()) time.sleep(1) r_lock.release() r_lock.release() def run(self): self.actionA() self.actionB() if __name__ == '__main__': r_lock = threading.RLock() List = [] for i in range(5): t = MyThread() t.start() List.append(t) for y in List: y.join() print("All end")
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· 三行代码完成国际化适配,妙~啊~
· .NET Core 中如何实现缓存的预热?