- A+
线程(threading)
进程是系统进⾏资源分配和调度的⼀个独⽴单位.
线程是进程的⼀个实体,是CPU调度和分派的基本单位,它是⽐进程更⼩的 能独⽴运⾏的基本单位.线程⾃⼰基本上不拥有系统资源,只拥有⼀点在运 ⾏中必不可少的资源(如程序计数器,⼀组寄存器和栈),但是它可与同属⼀ 个进程的其他的线程共享进程所拥有的全部资源.
与进程区别
⼀个程序⾄少有⼀个进程,⼀个进程⾄少有⼀个线程.
线程的划分尺度⼩于进程(资源⽐进程少),使得多线程程序的并发性⾼。
进程在执⾏过程中拥有独⽴的内存单元,⽽多个线程共享内存,从⽽极 ⼤地提⾼了程序的运⾏效率
线线程不能够独⽴执⾏,必须依存在进程中
线程和进程在使⽤上各有优缺点:线程执⾏开销⼩,但不利于资源的管理和 保护;⽽进程正相反。
Thread模块实现方式:
导入模块(import threading )
t=Thread(target= XXX) XXX需要执行的代码块
t.start()
例子中有睡眠1秒,但是加入线程后,执行时间缩短
定义一个类继承Thread
class MyThread(threading.Thread): 继承Thread类
def run(self ): 重写run方法
#代码
使用Start()执行
线程的执行顺序
多线程的执行顺序是不确定的
主线程会等子线程结束后才结束
查看线程的数量:
length=len(threading.enumerate()) #查看线程数量
为了让每个线程的封装性更完美,所以使⽤threading模块时,往往会定 义⼀个新的⼦类class,只要继承 threading.Thread 就可以了,然后重 写 run ⽅法
同步
在如图的代码中的执行结果如图:
在取消注释time.sleep()的运行结果不同,在没有注释时,w1还没执行结束,w2就开始执行,导致运行结果不一致,出现了线程共同调用一个数据会出现安全问题,这事就需要线程的同步,
互斥锁
当多个线程⼏乎同时修改某⼀个共享数据的时候,需要进⾏同步控制
保证多个线程安全访问竞争资源,最简单的同步机制是引⼊互 斥锁。 互斥锁为资源引⼊⼀个状态:锁定/⾮锁定
创建锁
lo=threading.Lock([blocking])
#锁定
lo.acquire()
其中,锁定⽅法acquire可以有⼀个blocking参数。 如果设定blocking为True,则当前线程会堵塞,直到获取到这个锁为⽌ (如果没有指定,那么默认为True) 如果设定blocking为False,则当前线程不会堵塞
#True表示堵塞 即如果这个锁在上锁之前已经被上锁了,那么这个线程会在这⾥⼀直等
#False表示⾮堵塞,即不管本次调⽤能够成功上锁,都不会卡在这,⽽是继续执⾏下
#释放
lo.release()
上锁解锁过程 当⼀个线程调⽤锁的acquire()⽅法获得锁时,锁就进⼊“locked”状态。 每次只有⼀个线程可以获得锁。如果此时另⼀个线程试图获得这个锁,该线 程就会变为“blocked”状态,称为“阻塞”,直到拥有锁的线程调⽤锁的 release()⽅法释放锁之后,锁进⼊“unlocked”状态。 线程调度程序从处于同步阻塞状态的线程中选择⼀个来获得锁,并使得该线 程进⼊运⾏(running)状态。
锁的好处: 确保了某段关键代码只能由⼀个线程从头到尾完整地执⾏
锁的坏处:
阻⽌了多线程并发执⾏,包含锁的某段代码实际上只能以单线程模式执 ⾏,效率就⼤⼤地下降了
由于可以存在多个锁,不同的线程持有不同的锁,并试图获取对⽅持有 的锁时,可能会造成死锁
在多线程开发中,全局变量是多个线程都共享的数据,⽽局部变量等是 各⾃线程的,是⾮共享的
死锁
在线程间共享多个资源的时候,如果两个线程分别占有⼀部分资源并且同时 等待对⽅的资源,就会造成死锁。 尽管死锁很少发⽣,但⼀旦发⽣就会造成应⽤的停⽌响应。
避免死锁
程序设计时要尽量避免(银⾏家算法) 添加超时时间等