------------恢复内容开始------------
线程基础
1.1线程状态
线程有五种状态:新建、就绪、运行、阻塞、死亡
1.2线程同步
因为线程同时运行多个任务,但实际上还是cpu以极快的速度在每个线程之间转换处理任务,对于python这种高级语言,每条语句的执行都不是简单的一步到位,因此,在语句执行过程中,还未结束(比如打印两句话),cpu就运行其他的线程,这可能会导致公共资源发生不好的变化(第一句话还没完,中间就插入了第二句话),也就是数据不同步了,因此引用了锁的概念,避免重要数据在处理时被打断或者其他线程篡改。
锁有两种状态——锁定、未锁定,当锁定的时候,其他的线程执行到锁定的部分,需要请求锁,但是锁一次只供给一条线程,等这条先线程执行到释放锁的时候,其他的等待(被阻塞)的线程中,才有一条线程可以获得锁(线程进行竞争来获取锁,不知道那线程得到锁),然后继续执行、释放等,这样的话重要数据的操做会完整执行,保证了数据的同步。
线程和锁的关系
1.3线程通信(条件变量)
线程同步使用了锁,保证了只让一条线程执行,其他等待,我们认为其他的线程被阻塞了,且这些线程的等待是被动的,释放锁后会立刻竞争锁。虽然能保证线程不干扰了,但是有一个问题,我们怎么能保证最开始执行的线程时我们向要执行的线程呢,毕竟,线程执行的任务不一定全部一样,有的线程需要其他线程创造一些条件,才能正常无错误的执行,比如a线程创建了一个列表,b线程才可以遍历列表,如果b线程先遍历,那么就会报错。因此引入了条件变量的概念
条件变量,允许在不满足条件的时候,线程进入等待,当其他线程创造条件后,发出通知,收到通知的线程直到可以干活了,就从挂起状态运行,这时候,如不收不到通过通知,就会一直挂着。
当一个线程得到锁定后,判断是否满足条件。如果不满足,则回到等待池(此时自动解锁),等待条件满足的通知,然后收到通知后,请求锁定,直到获得锁定,条件满足,继续执行,然后释放锁给其它线程
挂起相对于阻塞来说,可以看为是一个主动的行为,因为如果一致不满足,及时锁释放了,被挂起的线程也不会取竞争锁
当满足某种条件后,认为需要唤醒等待线程来执行任务,就可以使用 nodify() 后者nodify_all()唤起一条或所等待池中的线程
1.4、线程阻塞之间的转换
阻塞有三种情况:
同步阻塞是指处于竞争锁定的状态,线程请求锁定时将进入这个状态,一旦成功获得锁定又恢复到运行状态;
等待阻塞是指等待其他线程通知的状态,线程获得条件锁定后,调用“等待”将进入这个状态,一旦其他线程发出通知,线程将进入同步阻塞状态,再次竞争条件锁定;
而其他阻塞是指调用time.sleep()、anotherthread.join()或等待IO时的阻塞,这个状态下线程不会释放已获得的锁定。
tips: 如果能理解这些内容,接下来的主题将是非常轻松的;并且,这些内容在大部分流行的编程语言里都是一样的。(意思就是非看懂不可 >_< 嫌作者水平低找别人的教程也要看懂)
python多线程——threading使用
概述:
threading用于提供跟线程相关的操作,线程是应用程序中工作的最小单元。
threading模块提供的类
Thread、Lock、Rlock、Condition、Semaphore、Event、Timer、local
threading模块提供的 常用方法
threading.currentThreading() 返回现在进行的线程变量
threading.enumerate() 返回当前正在运行的所有进程的列表
threading.activeCount() 返回活跃的线程的数量,与len(threading.enumrate())相同
Thread类
Thread类是线程类,使用方式为传入要运行的方法和继承Thread
import threading import time # method1 将要执行的方法,作为一个参数,在实例化Threading类的时候,作为目标直接传入 def loop(x,y): print('%s is running' % threading.currentThread()) time.sleep(3) print('%s is end' % threading.currentThread()) return x+y for i in range(4): t = threading.Thread(target=loop,name='线程'+str(i),args=(i,5)) t.start() time.sleep(1) print() print('\nmian thread end') # method2 从Thread类继承,继承的对象重写的调用run方法 class LoopClass(threading.Thread): def __init__(self,name): super(LoopClass,self).__init__() # 显式调用父类初始化函数(暂时不理解为什么) self.name = name def run(self) -> None: print(self.name,'is runnig' ) print('当前进程有======>',threading.enumerate()) for i in range(4,7): l=LoopClass(i) l.run() # # <Thread(线程0, started 10984)> is running # # <Thread(线程1, started 12096)> is running # # <Thread(线程2, started 956)> is running # <Thread(线程0, started 10984)> is end # # <Thread(线程3, started 4256)> is running # <Thread(线程1, started 12096)> is end # # # mian thread end # 4 is runnig # 当前进程有======> [<_MainThread(MainThread, started 12068)>, <Thread(线程2, started 956)>, <Thread(线程3, started 4256)>] # 5 is runnig # 当前进程有======> [<_MainThread(MainThread, started 12068)>, <Thread(线程2, started 956)>, <Thread(线程3, started 4256)>] # 6 is runnig # 当前进程有======> [<_MainThread(MainThread, started 12068)>, <Thread(线程2, started 956)>, <Thread(线程3, started 4256)>] # <Thread(线程2, started 956)> is end # <Thread(线程3, started 4256)> is end
Thread类构造方法
Thread(group=None,target=None,name=None,args=(),kwarg={})
group:线程组,当前未实现,默认为None
target:可执行的方法
name:线程名
实例方法:
isAlive() 线程是否活跃
setName() 设置线程名
getName() 获取线程名
start() 启动线程
is/setDeamon(bool) 获取/设置为后台线程(默认前台线程为False),在start之前设置
后台线程(守护线程):相当于线程的一种属性,当主线程执行完的时候,如果后台线程没有执行结束,也会结束程序
前台线程:当主线程执行结束的时候,如果有前台线程还在执行,则等待前台线程执行结束,然后程序结束
def loop(n): while n: print('%s is running' % threading.currentThread()) time.sleep(2) return 1 for i in range(4): t = threading.Thread(target=loop,name='线程'+str(i),args=(1,)) t.setDaemon(True) # 设置为守护进程, t.start() for i in range(4): td = threading.Thread(target=loop,name='线程'+str(i),args=(2,)) td.setDaemon(True) td.start() print('\nmian thread end') # 当两个进程都无限循环,但是都被设置为守护进程,主线程不会无限等待,正常终止 # <Thread(线程0, started daemon 4132)> is running # <Thread(线程1, started daemon 11216)> is running # <Thread(线程2, started daemon 11640)> is running # <Thread(线程3, started daemon 5844)> is running # mian thread end # 当某一个进程设置为前台进程,即便主线程执行完,程序也不会结束
join([timeout]) 阻塞上下文环境的线程,直到调用此方法的线程终止,或者达到timeout,(及时线程设置守护线程,也需要等待)
import threading import time def loop1(): # time.sleep(1) print('This is %s is running' % threading.currentThread().getName()) time.sleep(1) print('%s ending' % threading.currentThread().getName()) thread_list = [] for i in range(4): a = threading.Thread(target=loop1) thread_list.append(a) a.start() for thread in thread_list: thread.join() print('this is main ') # This is Thread-1 is running # This is Thread-2 is running # This is Thread-3 is running # This is Thread-4 is running # Thread-1 endingThread-2 endingThread-3 ending # # # Thread-4 ending # this is main # # 进程已结束,退出代码为 0
Lock和RLock类
线程之间随机调度,某条线程执行的语句还未结束,cpu就转换到其他的线程上了,为了避免操作同一个线程的时候,资源不产生混乱,使用锁,保证公共的变量不被多条线程同时执行。
Lock(指令锁),是最低级的同步指令,当Lock处在锁定状态的时候,不被特定的线程所有,.Lock包含了锁定、非锁定两种状态,以及aquest和release两种方法。请求锁定时,线程将处于锁定状态,直到释放
RLock(可重入锁)可以被一个线程对此请求的同步指令。RLock有”拥有的线程“和”递归等级“的概念,RLock被某个线程所有,该线程可以再次调用acquire(),release()时需要相同的次数
可以认为RLock包含一个计数器,当成功调用acquire/release时,计数器+1/-1,当计数器为0时,认为是未锁定状态
相对于Lock,RLock可以重复aquire而不会出现死锁的情况
lock.require([,timeout])
lock.release()
import threading lock = threading.Lock() # Lock对象 lock.acquire() print(1) lock.acquire() # 产生了死锁。 print(2) lock.release() print(3) lock.release() print(lock.acquire()) # 无法结束 # 1
import threading
lock = threading.RLock() # RLock对象
lock.acquire()
print(1)
lock.acquire()
print(2)
lock.release()
print(3)
lock.release()
print(lock.acquire())
# 正常结束
# 1
# 2
# 3
# True
Condition
condition(条件变量),是一个与锁关联的
可以认为,condition是更强大的带锁功能的类,与condition相关的概念有线程的阻塞和挂起
阻塞:因为资源的争抢,线程被动的停止。当一个线程获得了锁,其他的线程都需要等待这个线程释放锁之后才能进行,那么这些等待的线程就是被锁定了
挂起:主动的行为,可以主动挂起某个线程,当这个线程暂时不运行,也能主动放下线程让其继续运行
挂起的层级是高于阻塞的:当一个线程阻塞的时候,是可以被挂起的,挂起结束后,如果线程还在阻塞就不会运行。当一个线程挂起的时候,如果这个线程,在挂起期间阻塞消除了,如果不放下的话,线程仍旧无法运行
构造方法Condition([,Lock/RLock])
实例方法:
acquire([,timeout])/release: 请求锁、释放锁
wait([,timeout]):获取锁定之后,调用此方法,锁定的线程将自动解锁,并处于挂起状态,收到广播之后被唤醒,或者timeout超时,才能被唤醒,唤醒后的线程会重新获得锁)
notify():通知,调用该方法之后,会随机唤醒一个被挂起的线程,只是唤起,而不涉及释放锁,使用前先成功要已经获得锁
notify_all:唤起所有挂起的线程,使用前线程要已经获得锁
Event
线程通信机制,一个线程通知事件,其他线程等待事件,Event内置一个False的标志,当调用set()的时候,设置为True,调用Clear的是时候,设置为False,wait([,timeout]) 将根据Event是False还是True,将线程阻塞
实例方法
isSet() : 当内置事件为True时返回True
set():将event内置事件设置为True
clear():将event内置事件设置为False
wait():如果event为False,将阻塞线程,否则立刻返回
------------恢复内容结束------------