1.1 线程
线程是一个基本的 CPU 执行单元,它必须依托于进程存活。一个线程是一个execution context(执行上下文),即一个 CPU 执行时所需要的一串指令。
1.2 进程
进程是指一个程序在给定数据集合上的一次执行过程,是系统进行资源分配和运行调用的独立单位。可以简单地理解为操作系统中正在执行的程序。也就说,每个应用程序都有一个自己的进程。
每一个进程启动时都会最先产生一个线程,即主线程。然后主线程会再创建其他的子线程。
1.3 两者的区别
(1).线程必须在某个进行中执行。
(2).一个进程可包含多个线程,其中有且只有一个主线程。
(3).多线程共享同个地址空间、打开的文件以及其他资源。
(4).多进程共享物理内存、磁盘、打印机以及其他资源。
1.4 线程的类型
线程的因作用可以划分为不同的类型。大致可分为:
(1)主线程
(2)子线程
(3)守护线程(后台线程)
(4)前台线程
2.3 线程合并
Join函数执行顺序是逐个执行每个线程,执行完毕后继续往下执行。主线程结束后,子线程还在运行,join函数使得主线程等到子线程结束时才退出。
# 将 t1 和 t2 加入到主线程中
t1.join() t2.join()
2.4 线程同步与互斥锁
线程之间数据共享的。当多个线程对某一个共享数据进行操作时,就需要考虑到线程安全问题。threading模块中定义了Lock 类,提供了互斥锁的功能来保证多线程情况下数据的正确性。用法的基本步骤:
#创建锁
mutex = threading.Lock()
#锁定
mutex.acquire([timeout])
#释放
mutex.release()
2.6 守护线程
如果希望主线程执行完毕之后,不管子线程是否执行完毕都随着主线程一起结束。我们可以使用setDaemon(bool)函数,它跟join函数是相反的。它的作用是设置子线程是否随主线程一起结束,必须在start()之前调用,默认为False。
python多线程
Thread类为线程的抽象类,其构造方法的参数target指向一个函数对象,即该线程的具体操作。此外还可以有args=来给target函数传参数。需要注意的是当传任何一个序列进去的话Thread会自动把它分解成单个单个的元素然后分解传给target函数。
通过Thread.Lock类来创建简单的线程锁。lock = threading.Lock()即可。在某线程start之前,让lock.acquire(),且lock在acquire()之后不能再acquire,否则会报错。当线程结束后调用lock.release()来释放锁就好了。一般而言,有锁的多线程场景可以提升一部分效率,但在写文件等时机下会有阻塞等待的情况。相比之下,无锁多线程场景可以进一步提升效率,但是可能会引起读写冲突等问题,所以要慎用。一定要确认各个线程间没有共同的资源之类的问题后再实行无锁多线程。
线程的阻塞和挂起,线程的这两个状态乍一看都是线程暂停不再继续往前运行,但是引起的原因不太一样。阻塞是指线程间互相的制约,当一个线程获得了锁,其他的线程就被阻塞了,而挂起是出于统一调度的考虑。换句话说,挂起是一种主动的行为,在程序中我们主动挂起某个线程然后可以主动放下让线程继续运行;而阻塞更多时候是被动发生的,当有线程操作冲突了那么必然是有一方要被阻塞的。从层级上看,挂起操作是高于阻塞的,也就说一个线程可以在阻塞的时候被挂起,然后被唤醒后依然是阻塞状态。如果在挂起过程中具备了运行条件(即不再阻塞),线程也不会往前运行。
Condition类的一些方法,首先是acquire和release,Condition内部也维护了一把锁,默认是RLock类,所有关联了同一个Condition对象的线程也都会遵守这把锁规定的来进行运行。
Condition.wait([timeout]) 这个方法一定要在获取锁定之后调用,调用这个方法的Condition对象所在的线程会被挂起并且释放这个线程获得的所有锁,直到接到通知被唤醒或者超时(如果设置了Timeout的话),当被唤醒之后线程将重新获取锁定。
Condition.notify() notify就是上面所说的通知,调用这个方法之后会唤醒一个被挂起的线程。线程的选择尚不明确,似乎是随机的。需要注意的是notify方法只进行挂起的唤醒而不涉及锁的释放。
Condition.notify_all() 唤醒所有挂起的线程
python线程的事件(event)用于主线程控制其他线程的执行,事件主要提供了三个方法wait、clear、set
事件处理的机制:全局定义了一个“Flag”,如果“Flag”值为 False,那么当程序执行 event.wait 方法时就会阻塞,如果“Flag”值为True,那么event.wait 方法时便不再阻塞。
clear:将“Flag”设置为False
set:将“Flag”设置为True
用 threading.Event 实现线程间通信
使用threading.Event可以使一个线程等待其他线程的通知,我们把这个Event传递到线程对象中,Event默认内置了一个标志,初始值为False。
一旦该线程通过wait()方法进入等待状态,直到另一个线程调用该Event的set()方法将内置标志设置为True时,该Event会通知所有等待状态的线程恢复运行。
3.1 创建多进程
python中的多线程其实并不是真正的多线程,如果想要充分地使用多核CPU的资源,在python中大部分情况需要使用多进程。multiprocessing支持子进程、通信和共享数据、执行不同形式的同步,提供了Process、Queue、Pipe、Lock等组件。
(1).Process
创建进程的类:Process([group [, target [, name [, args [, kwargs]]]]]),target表示调用对象,args表示调用对象的位置参数元组。kwargs表示调用对象的字典。name为别名。group实质上不使用。
方法:is_alive()、join([timeout])、run()、start()、terminate()。其中,Process以start()启动某个进程。
属性:authkey、daemon(要通过start()设置)、exitcode(进程在运行时为None、如果为–N,表示被信号N结束)、name、pid。其中daemon是父进程终止后自动终止,且自己不能产生新进程,必须在start()之前设置。
Python 要进行多进程操作,需要用到muiltprocessing库,其中的Process类跟threading模块的Thread类很相似。所以直接看代码熟悉多进程。
3.2 多进程通信
进程之间不共享数据的。如果进程之间需要进行通信,则要用到Queue模块或者Pipi模块来实现。
Queue
Queue 是多进程安全的队列,可以实现多进程之间的数据传递。它主要有两个函数,put和get。
(1).put() 用以插入数据到队列中,put 还有两个可选参数:blocked 和 timeout。如果 blocked 为 True(默认值),并且 timeout 为正值,该方法会阻塞 timeout 指定的时间,直到该队列有剩余的空间。如果超时,会抛出 Queue.Full 异常。如果 blocked 为 False,但该 Queue 已满,会立即抛出 Queue.Full 异常。
(2).get()可以从队列读取并且删除一个元素。同样,get 有两个可选参数:blocked 和 timeout。如果 blocked 为 True(默认值),并且 timeout 为正值,那么在等待时间内没有取到任何元素,会抛出 Queue.Empty 异常。如果blocked 为 False,有两种情况存在,如果 Queue 有一个值可用,则立即返回该值,否则,如果队列为空,则立即抛出 Queue.Empty 异常。
from multiprocessing importProcess, Queue
def put (queue):
queue.put('Queue 用法')
if__name__ =='__main__':
queue = Queue()
pro = Process(target=put, args=(queue,))
pro.start()
print(queue.get())
pro.join()
作者:猴哥Yuri
链接:http://www.jianshu.com/p/a69dec87e646
來源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。