python多线程threading
python多线程threading
目录
threading介绍与简单使用
threading介绍:
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
import threading
def thread_job():
print("this is an added Thread ,number is %s" %threading.currentThread())
def main():
added_thread = threading.Thread(target=thread_job) # 定义一个新的线程,指定一个任务给target
added_thread.start() # 开启线程
print(threading.activeCount())
print(threading.enumerate())
print(threading.currentThread())
if __name__ =='__main__':
main()
程序运行结果:
第一个输出是当前线程,这个是我们开启的线程
第二个输出的是在正在运行的线程的数量
第三个输出返回一个包含正在运行的线程的list,包含主线程和开启的线程
第四个输出是当前线程,最后只剩下主线程
join功能
join功能介绍:
join([time]): 等待至线程中止。这阻塞调用线程直至线程的join() 方法被调用中止-正常退出或者抛出未处理的异常-或者是可选的超时发生。
当我们有一个程序功能必须等到开启的线程执行完以后,才能运行主线程,就可以使用这个功能。
不加join的情况,这种情况下,以下例程序看来,主线程运行更快,我们看看结果
程序示例1:
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:
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功能
线程之间的通信
queue功能:
线程优先级队列( 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()方法
程序示例:
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中实现多线程其实是把正在运行的线程锁住,这时候其他线程就不会运行,就是在同一时间里只有一个线程在运行,在不断地切换线程中就可以实现多线程。
下面我使用多线程和不使用线程,来做同样的运算工作,看谁运行的更快。
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")