线程和进程的区别与关系
1.进程是系统进行资源分配和调度的一个独立单位,进程是资源分配的单位,线程是cpu调度的单位。。
2.线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位.线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器和栈),但是它可与同属一个进程的其他的线程共享进程所拥有的全部资源.
3.进程和线程的关系
1.一个程序至少有一个进程,一个进程至少有一个线程(这个线程是主线程)。
2.线程的划分尺度小于进程(资源比进程少),使得多线程程序的并发性高。
3.进程在执行过程中拥有独立的内存单元,而多个线程共享内存,从而极大地提高了程 序的运行效。
4.线程不能够独立执行,必须依存在进程中。
线程和进程的优缺点
1.线程和进程在使用上各有优缺点:线程执行开销小,但不利于资源的管理和保护,而进程正相反。
2.线程运行出错异常后,如果没有捕获,会导致整个进程崩溃,程序退出;如果是进程出问题,只会退出当前进程。
4.python的
thread模块是比较底层的模块,python的threading模块是对thread做了一些包装的,可以更加方便的创建线程.
from threading import Thread
Thread类说明
1.
可以明显看出使用了多线程并发的操作,花费时间要短很多。
2.
创建好的线程,需要调用start()方法来启动。
3.
在不同子线程中打印当前进程都是同一个进程,父进程也是同一个。
4.
主线程会等待所有的子线程结束后才结束;也可以使用join()实现让其他线程(没有调用start的显示)和主线程等待当前子线程执行完毕,才往下执行。
自定义threading.Thread子类实现多线程
1.threading.currentThread(): 返回当前的线程变量。
2.threading.enumerate(): 返回一个包含正在运行的线程的list。正在运行指线程启动后、结束前,不包括启动前和终止后的线程。
使用 threading.enumerate()获取当前线程数量
from threading import Thread, enumerate
length = len(enumerate()) print("当前线程数:%d" % length)
3.threading.activeCount():返回正在运行的线程数量,与len(threading.enumerate())有相同的结果。
4.构造方法:
Thread(group=None, target=None, name=None, args=(), kwargs={})
group: 线程组,目前还没有实现,库引用中提示必须是None;
target: 要执行的方法;
name: 线程名;
args/kwargs: 要传入方法的参数。
实例方法:
isAlive(): 返回线程是否在运行。正在运行指启动后、终止前。
get/setName(name):获取/设置线程名。
start():线程准备就绪,等待CPU调度
join([timeout]):阻塞当前上下文环境的线程,直到调用此方法的线程终止或到达指定的timeout(可选参数)。
注意:1、自定义的线程类必须继承Tread
2、如果有__init__方法,注意调用super().__init__()
3、必须重写run方法
4、不传入名字则显示默认名字Thread-1,Thread-2……可以在初始话时设置名字也可以用setNname设置名字
线程不安全:多个线程对同一资源的访问,对数据造成破坏,使得线程运行的结果不可预期。这种现象称为“线程不安全”。
产生原因:
1、操作共享数据的代码有多句组成
2、两个线程共享一个数据
3、由于GIL(全局解释器)要把数据设置的大一点才能够看到结构
当多个线程对同一数据操作时,某个线程没有运行完成的时候被抢占执行权后再次执行的时候会在上次暂停后继续执行
控制线程执行先后顺序--解决线程安全问题使用sleep方式先让线程某一线程执行完成
while Ture 轮询 用标志位方式上锁(线程上锁不让其他线程操作该数据,处理完在解锁)
可以通过线程同步来进行解决线程不安全问题
线程同步,让线程按预定的先后次序进行运行,最终得到预期的结果,这是线程的同步。
互斥锁
1.当多个线程几乎同时修改某一个共享数据的时候,需要进行同步控制。
2.线程同步能够保证多个线程安全访问竞争资源,最简单的同步机制是引入互斥锁。
3.互斥锁为资源引入一个状态:锁定/非锁定。
4.某个线程要更改共享数据时,先将其锁定,此时资源的状态为“锁定”,其他线程不能更改;直到该线程释放资源,将资源的状态变成“非锁定”,其他的线程才能再次锁定该资源。互斥锁保证了每次只有一个线程进行写入操作,从而保证了多线程情况下数据的正确性。
其中,锁定方法acquire可以有一个blocking参数。
如果设定blocking为True,则当前线程会堵塞,直到获取到这个锁为止(如果没有指定,那么默认为True)如果设定blocking为False,则当前线程不会堵塞
当一个线程调用锁的acquire()方法获得锁时,锁就进入“locked”状态。
每次只有一个线程可以获得锁。如果此时另一个线程试图获得这个锁,该线程就会变为“blocked”状态,称为“阻塞”,直到拥有锁的线程调用锁的release()方法释放锁之后,锁进入“unlocked”状态。
线程调度程序从处于同步阻塞状态的线程中选择一个来获得锁,并使得该线程进入运行(running)状态。
from threading import Lock
#创建锁
mutex = Lock()锁定为Ture
#锁定,可以设置阻塞和非阻塞,默认是阻塞
#还可以设置超时
mutex.acquire(timeout(延时)=1) #多个锁要解锁在上锁否则下一个锁会处在阻塞状态
#释放
mutex.release()
互斥锁的好坏
1.锁的好处:确保了某段关键代码只能由一个线程从头到尾完整地执。
2.锁的坏处:阻止了多线程并发执行,包含锁的某段代码实际上只能以单线程模式执行,效率就大大地下降了,由于可以存在多个锁,不同的线程持有不同的锁,并试图获取对方持有的锁时,可能会造成死锁
3.等待解锁的方式(通知?轮询?)是通知方式,通知性能要高一些。一个释放,所有的线程都在抢锁。只有多线程同时对一个资源同时进行修改的时候需要加锁;加锁的代码越短越好,原则能不加的就不加,
多线程对局部变量数据-非共享
1.
全局变量是多个线程都共享的数据;
2.
而局部变量等是各自线程的,类属性是共享的,实例属性是非共享的。各个实例对象彼此间数据也是不共享的。
死锁
开发过程中使用线程,在线程间共享多个资源的时候,如果两个线程分别占有一部分资源并且同时等待对方的资源,就会造成死锁。
死锁状态,可以使用ctrl-z退出。出现死锁是无法解决的,只能在程序设计时要尽量避免,添加超时时间等可以避免死锁的情况。
threading.local()
1. 使用函数传参的方法-传递参数,传递起来很麻烦:
2. 使用全局字典的方法-传递参数 最大的优点是消除了std对象在每层函数中的传递问题,但是,每个函数获取std的代码有点low。
3. 使用全局threading.local()的方法 local_school = threading.local()
一个threading.local()变量虽然是全局变量,但每个线程都只能读写自己线程的独立副本,互不干扰。threading.local()解决了参数在一个线程中各个函数之间互相传递的问题。
同步和异步的概念同步和异步关注的是消息通信机制
同步,就是在发出一个“调用”时,在没有得到结果之前,该“调用”就不返回。但是一旦调用返回,就得到返回值了。
异步则是相反,“调用”在发出之后,这个调用就直接返回了,所以没有返回结果。
GIL的问题
为了python能在多核cpu上正常运行和数据没有问题,引入了GIL,它相当于一把全局锁,让每个线程轮流去多核cpu中的某个cpu执行,从而导致cpu并没有占满的原因,相当于python里的多线程假的