线程概述
多线程相似于同时执行多个不同程序,多线程运行有如下长处:
应用线程能够把占据长时间的程序中的工作放到后盾去解决。
用户界面能够更加吸引人,这样比方用户点击了一个按钮去触发某些事件的解决,能够弹出一个进度条来显示解决的进度
程序的运行速度可能放慢
在一些期待的工作实现上如用户输出、文件读写和网络收发数据等,线程就比拟有用了。在这种状况下咱们能够开释一些宝贵的资源如内存占用等等。
线程在执行过程中与过程还是有区别的。每个独立的过程有一个程序运行的入口、程序执行序列和程序的进口。然而线程不可能独立执行,必须依存在应用程序中,由应用程序提供多个线程执行管制。
每个线程都有他本人的一组CPU寄存器,称为线程的上下文,该上下文反映了线程上次运行该线程的CPU寄存器的状态。
指令指针和堆栈指针寄存器是线程上下文中两个最重要的寄存器,线程总是在过程失去上下文中运行的,这些地址都用于标记领有线程的过程地址空间中的内存。
线程能够被抢占(中断)。
在其余线程正在运行时,线程能够临时搁置(也称为睡眠) -- 这就是线程的让步。
线程就是在程序运行过程中,执行程序代码的一个分支,每个运行的程序至多都有一个线程
创立多线程:thread调用thread模块中的start_new_thread()函数来产生新线程thread.start_new_thread ( function, args[, kwargs] )function - 线程函数。args - 传递给线程函数的参数,他必须是个tuple类型。kwargs - 可选参数。python多线程的开启start()
import threadingimport timedef task(): time.sleep(1) print("以后线程:", threading.current_thread().name)if__name__ =='__main__': for_inrange(5): sub_thread = threading.Thread(target=task) sub_thread.start()
线程之间执行是无序的
主线程会期待所有的子线程完结后才完结
主线程会期待所有的子线程完结后才完结,如果须要能够设置守护主线程
多线程的特点(共享全局变量)
threading.currentThread(): 返回以后的线程变量。threading.enumerate(): 返回一个蕴含正在运行的线程的list。正在运行指线程启动后、完结前,不包含启动前和终止后的线程。threading.activeCount(): 返回正在运行的线程数量,与len(threading.enumerate())有雷同的后果。
除了应用办法外,线程模块同样提供了Thread类来解决线程,Thread类提供了以下办法:
run(): 用以示意线程流动的办法。start():启动线程流动。join([time]): 期待至线程停止。这阻塞调用线程直至线程的join() 办法被调用停止-失常退出或者抛出未解决的异样-或者是可选的超时产生。isAlive(): 返回线程是否流动的。getName(): 返回线程名。setName(): 设置线程名。
自定义线程
自定义线程不能指定target,因为自定义线程外面的工作都对立在run办法外面执行
启动线程对立调用start办法,不要间接调用run办法, 因为这样不是应用子线程去执行工作
import threading# 自定义线程类classMyThread(threading.Thread):# 通过构造方法取接管工作的参数 def__init__(self, info1, info2): # 调用父类的构造方法 super(MyThread, self).__init__() self.info1 = info1 self.info2 = info2 # 定义自定义线程相干的工作 def test1(self): print(self.info1) deftest2(self): print(self.info2) # 通过run办法执行相干工作 def run(self): self.test1() self.test2()
创立自定义线程
my_thread = MyThread("测试1","测试2")
启动
my_thread.start()
资源竞争问题
多线程能够共享全局变量
多线程共享全局变量,很不便在多个线程间共享数据
因为多线程同时对全局变量进行操作,很容易呈现资源竞争问题
import threading# 定义全局变量g_num =0# 循环一次给全局变量加1defsum_num1(): for i in range(1000000): globalg_num g_num +=1 print("sum1:", g_num)# 循环一次给全局变量加1defsum_num2(): for i in range(1000000): global g_num g_num +=1 print("sum2:", g_num)if__name__ =='__main__':# 创立两个线程first_thread = threading.Thread(target=sum_num1) second_thread = threading.Thread(target=sum_num2)# 启动线程first_thread.start()# 启动线程second_thread.start()
咱们能够看到多线程同时对全局变量操作数据产生了谬误
多线程同时操作全局变量导致数据可能呈现谬误的起因剖析
两个线程first_thread和second_thread都要对全局变量g_num(默认是0)进行加1运算,然而因为是多线程同时操作,有可能呈现上面状况:
在g_num=0时,first_thread获得g_num=0。此时零碎把first_thread调度为”sleeping”状态,把second_thread转换为”running”状态,t2也取得g_num=0
而后second_thread对失去的值进行加1并赋给g_num,使得g_num=1
而后零碎又把second_thread调度为”sleeping”,把first_thread转为”running”。线程t1又把它之前失去的0加1后赋值给g_num。
这样导致尽管first_thread和first_thread都对g_num加1,但后果依然是g_num=1
多线程资源竞争解决办法
线程同步
线程同步: 保障同一时刻只能有一个线程去操作全局变量 同步: 就是协同步调,按预约的先后秩序进行运行。如:你说完,我再说, 好比现实生活中的对讲机
多个线程同时对同一个全局变量进行操作,会有可能呈现资源竞争数据谬误的问题
线程同步形式能够解决资源竞争数据谬误问题,然而这样有多任务变成了单任务。
互斥锁
threading模块中定义了Lock变量,这个变量实质上是一个函数,能够不便的解决锁定:
创立锁
mutex = threading.Lock()
锁定
mutex.acquire()
开释
mutex.release()
import threading# 定义全局变量g_num =0# 创立全局互斥锁lock = threading.Lock()# 循环一次给全局变量加1def sum_num1():# 上锁 lock.acquire() for i in range(1000000): global g_num g_num +=1 print("sum1:", g_num)# 开释锁 lock.release()# 循环一次给全局变量加1def sum_num2():# 上锁 lock.acquire() for iin range(1000000): global g_num g_num +=1 print("sum2:", g_num)# 开释锁 lock.release()if __name__ =='__main__':# 创立两个线程 first_thread = threading.Thread(target=sum_num1)second_thread = threading.Thread(target=sum_num2)# 启动线程 first_thread.start()second_thread.start()
提醒:加上互斥锁,哪个线程抢到这个锁咱们决定不了,哪个线程抢到锁哪个线程先执行,没有抢到的线程须要期待
# 加上互斥锁多任务霎时变成单任务,性能会降落,也就是说同一时刻只能有一个线程去执行
当一个线程调用锁的acquire()办法取得锁时,锁就进入“locked”状态。
每次只有一个线程能够取得锁。如果此时另一个线程试图取得这个锁,该线程就会变为“blocked”状态,称为“阻塞”,直到领有锁的线程调用锁的release()办法开释锁之后,锁进入“unlocked”状态。
线程调度程序从处于同步阻塞状态的线程中抉择一个来取得锁,并使得该线程进入运行(running)状态。
确保了某段要害代码只能由一个线程从头到尾残缺地执行
锁的害处:
多线程执行变成了蕴含锁的某段代码实际上只能以单线程模式执行,效率就大大地降落了
锁应用不好就容易呈现死锁状况
死锁
import threadingimport time# 创立互斥锁lock = threading.Lock()# 依据下标去取值, 保障同一时刻只能有一个线程去取值def get_value(index):# 上锁 lock.acquire() print(threading.current_thread()) my_list = [3,6,8,1]# 判断下标开释越界 if index >=len(my_list): print("下标越界:", index) return value = my_list[index] print(value) time.sleep(0.2)# 开释锁 lock.release()if __name__ =='__main__':# 模仿大量线程去执行取值操作 for i in range(30): sub_thread = threading.Thread(target=get_value,args=(i,)) sub_thread.start()
应用互斥锁的时候须要留神死锁的问题,要在适合的中央留神开释锁
死锁一旦产生就会造成利用的进行响应
线程优先级队列( Queue)
Python的Queue模块中提供了同步的、线程平安的队列类,包含FIFO(先入先出)队列Queue,LIFO(后入先出)队列LifoQueue,和优先级队列PriorityQueue。这些队列都实现了锁原语,可能在多线程中间接应用。能够应用队列来实现线程间的同步。
Queue模块中的罕用办法:
Queue.qsize() 返回队列的大小Queue.empty() 如果队列为空,返回True,反之FalseQueue.full() 如果队列满了,返回True,反之FalseQueue.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() 实际上意味着等到队列为空,再执行别的操作
线程,你学废了吗?