线程概述

多线程相似于同时执行多个不同程序,多线程运行有如下长处:

应用线程能够把占据长时间的程序中的工作放到后盾去解决。

用户界面能够更加吸引人,这样比方用户点击了一个按钮去触发某些事件的解决,能够弹出一个进度条来显示解决的进度

程序的运行速度可能放慢

在一些期待的工作实现上如用户输出、文件读写和网络收发数据等,线程就比拟有用了。在这种状况下咱们能够开释一些宝贵的资源如内存占用等等。

线程在执行过程中与过程还是有区别的。每个独立的过程有一个程序运行的入口、程序执行序列和程序的进口。然而线程不可能独立执行,必须依存在应用程序中,由应用程序提供多个线程执行管制。

每个线程都有他本人的一组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() 实际上意味着等到队列为空,再执行别的操作

线程,你学废了吗?