python基础---->进程、线程及相关等
基本概念
进程
1、什么是进程?
程序并不能单独运行,只有将程序装载到内存中,系统为它分配资源才能运行,而这种执行的程序就称之为进程。
程序和进程的区别就在于:程序是指令的集合,它是进程运行的静态描述文本;进程是程序的一次执行活动,属于动态概念。
简单点说就是:进程就是一个程序在一个数据集上的一次动态执行过程。
2、进程的特点:
一个时间点只能做一件事,不能同时干两件及以上的事;
在执行的过程进程如果阻塞,例如等待输入,整个进程就会挂起,即使进程中有些工作不依赖于输入的数据,也将无法执行。
线程
1、什么是线程(Thread)?
线程也叫轻量级进程,它是一个基本的CPU执行单元,也是程序执行过程中的最小单元;
一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务;
2、线程的特点:
突破一个进程只能干一样事的缺陷,使到进程内并发成为可能;
线程的引入减小了程序并发执行时的开销,提高了操作系统的并发性能;
进程和线程的区别
线程是执行的指令集 , 进程是资源的集合
线程的启动速度要比进程的启动速度要快
两个线程的执行速度是一样的,进程与线程的运行速度是没有可比性的
线程共享创建它的进程的内存空间 , 进程的内存是独立的
两个线程共享的数据都是同一份数据 , 两个子进程的数据不是共享的 , 而且数据是独立的
同一个进程的线程之间可以直接交流 , 同一个主进程的多个子进程之间是不可以进行交流 , 如果两个进程之间需要通信 , 就必须要通过一个中间代理来实现
一个新的线程很容易被创建 , 一个新的进程创建需要对父进程进行一次克隆
一个线程可以控制和操作同一个进程里的其他线程 , 线程与线程之间没有隶属关系 , 但是进程只能操作子进程
改变主线程 , 有可能会影响到其他线程的行为 , 但是对于父进程的修改是不会影响子进程
python threading模块
线程有两种调用方式:
直接调用:
import threading import time def run(n): #定义线程要运行的函数 print('task',n) time.sleep(2) if __name__ == '__main__': t1 = threading.Thread(target=run,args=(1,)) #生成一个线程 t2 = threading.Thread(target=run,args=(2,)) #生成一个另外线程 t1.start() # 启动线程 t2.start() # 启动另外一个线程 print(t1.getName()) # 获取线程名 print(t2.getName()) print('I am main thread') #主线程 #这个进程里面有三个线程,1个主线程,t1,t2两个子线程 #子线程和主线程是同步开启的,主线程结束后,要等子线程全部结束后,进程才会关闭
PS:线程相关的方法:
start 线程准备就绪,等待CPU调度 setName 为线程设置名称 getName 获取线程名称 run 线程被cpu调度后自动执行线程对象的run方法 # 下面会讲到 setDaemon 将线程声明为守护线程,必须在start() 方法调用之前设setDaemon(),只要主线程完成了,不管子线程是否完成,都要和主线程一起退出
join 逐个执行每个线程,执行完毕后继续往下执行,该方法保证当前线程执行完成后再执行其它线程,使得多线程变得无意义。
继承式调用:
import threading import time class MyThread(threading.Thread): #继承threading,Thread模块 def __init__(self,n): threading.Thread.__init__() #继承父类 # 或者super(MyThread, self).__init__() self.n = n def run(self): #定义每个线程要运行的函数,必须用run print('task',self.n) time.sleep(2) if __name__ == '__main__': t1 = MyThread(1) t2 = MyThread(2) t1.start() t2.start() print('I am main thread')
jion和setDaemon:
主线程:当一个程序启动时 , 就有一个进程被操作系统创建 , 与此同时一个线程也立刻运行 , 该线程通常叫做程序的主线程;
子线程 : 因为程序是开始时就执行的 , 如果你需要再创建线程 , 那么创建的线程就是这个主线程的子线程;
join应用:
import threading import time def run(n): print("task ",n ) time.sleep(2) start_time = time.time() t_objs = [] # 存线程实例 for i in range(10): #生成10个线程 t = threading.Thread(target=run,args=("t-%s" %i ,)) t.start() t_objs.append(t) #为了不阻塞后面线程的启动,不在这里join,先放到一个列表里 for t in t_objs: #循环线程实例列表,等待所有线程执行完毕 t.join() print("---all threads has finished...") print("cost:",time.time() - start_time)
setDaemon应用:
import threading import time def run(n): print('task',n) time.sleep(2) print('i am 子线程') #主线程结束,setDaemon不管有没有运行完都会被销毁 if __name__ == '__main__': t1 = threading.Thread(target=run,args=(1,)) t2 = threading.Thread(target=run,args=(2,)) t1.setDaemon(True) #设置守护线程,放在start之前 t1.start() t2.setDaemon(True) t2.start() print('I am main thread') 结果: task 1 task 2 I am main thread
线程锁
lock:如果有多个进程对同一文件进行修改 , 就会造成错乱 , 所以我们为了保护文件数据的安全 , 就需要给其进行加锁,join为整体串行 , lock为局部串行
Rlock(递归锁):在线程间共享多个资源的时候,如果两个线程分别占有一部分资源并且同时等待对方的资源,就会造成死锁,因为系统判断这部分资源都正在使用,所有这两个线程在无外力作用下将一直等待下去。就是解决死锁的问题
加锁:
import time import threading def addNum(): global num #在每个线程中都获取这个全局变量 print('--get num:',num ) time.sleep(1) lock.acquire() #修改数据前加锁 num -=1 #对此公共变量进行-1操作 lock.release() #修改后释放 num = 100 #设定一个共享变量 thread_list = [] lock = threading.Lock() #生成全局锁 for i in range(100): t = threading.Thread(target=addNum) t.start() thread_list.append(t) for t in thread_list: #等待所有线程执行完毕 t.join() print('final num:', num )
Semaphore(信号量)
互斥锁 同时只允许一个线程更改数据,而Semaphore是同时允许一定数量的线程更改数据 ,比如食堂打饭有5个窗口,那最多只允许5个人同时打饭,后面的人只能等里面有人打完才能去打。
import threading, time def run(n): semaphore.acquire() time.sleep(1) print("run the thread: %s\n" % n) semaphore.release() if __name__ == '__main__': semaphore = threading.BoundedSemaphore(5) # 最多允许5个线程同时运行 for i in range(20): t = threading.Thread(target=run, args=(i,)) t.start() while threading.active_count() != 1: pass else: print('----all threads done---')
Queue(队列)
Queue是python标准库中的线程安全的队列(FIFO)实现,提供了一个适用于多线程编程的先进先出的数据结构,即队列,用来在生产者和消费者线程之间的信息传递
基本FIFO队列
FIFO即First in First Out,先进先出。Queue提供了一个基本的FIFO容器,使用方法很简单,maxsize是个整数,指明了队列中能存放的数据个数的上限。一旦达到上限,插入会导致阻塞,直到队列中的数据被消费掉。如果maxsize小于或者等于0,队列大小没有限制。
简单的FIFO:
import Queue q = Queue.Queue() for i in range(5): q.put(i) while not q.empty(): print q.get()
0
1
2
3
4
LIFO队列
LIFO即Last in First Out,后进先出。与栈的类似;
简单的LIFO:
import Queue q = Queue.LifoQueue() for i in range(5): q.put(i) while not q.empty(): print q.get()
4
3
2
1
0
优先级队列
import Queue import threading class Job(object): def __init__(self, priority, description): self.priority = priority self.description = description print('Job:',description) return def __cmp__(self, other): return cmp(self.priority, other.priority) q = Queue.PriorityQueue() q.put(Job(3, 'level 3 job')) q.put(Job(10, 'level 10 job')) q.put(Job(1, 'level 1 job')) def process_job(q): while True: next_job = q.get() print('for:', next_job.description) q.task_done() workers = [threading.Thread(target=process_job, args=(q,)), threading.Thread(target=process_job, args=(q,)) ] for w in workers: w.setDaemon(True) w.start() q.join()
Job: level 3 job Job: level 10 job Job: level 1 job for: level 1 job for: level 3 job for: job: level 10 job
常用方法
qsize() #看队列大小 task_done() # 意味着之前入队的一个任务已经完成。由队列的消费者线程调用。
# 每一个get()调用得到一个任务,接下来的task_done()调用告诉队列该任务已经处理完毕。如果当前一个join()正在阻塞,它将在队列中的所有任务都处理完时恢复执行(即每一个由put()调用入队的任务都有一个对应的task_done()调用)。 join() # 阻塞调用线程,直到队列中的所有任务被处理掉。只要有数据被加入队列,未完成的任务数就会增加。当消费者线程调用task_done()(意味着有消费者取得任务并完成任务),未完成的任务数就会减少。当未完成的任务数降到0,join()解除阻塞。 put(item[, block[, timeout]]) # 将item放入队列中。 # 1、如果可选的参数block为True且timeout为空对象(默认的情况,阻塞调用,无超时)。 # 2、如果timeout是个正整数,阻塞调用进程最多timeout秒,如果一直无空空间可用,抛出Full异常(带超时的阻塞调用)。 # 2、如果block为False,如果有空闲空间可用将数据放入队列,否则立即抛出Full异常其非阻塞版本为put_nowait等同于put(item, False) get([block[, timeout]]) # 从队列中移除并返回一个数据。block跟timeout参数同put方法 # 其非阻塞方法为`get_nowait()`相当与get(False) full() #判断队列是否有数据 返回bool值 empty() # 如果队列为空,返回True,反之返回False
Event(事件)
python线程的事件用于主线程控制其他线程的执行,事件主要提供了三个方法 set、wait、clear。
事件处理的机制:全局定义了一个“Flag”,如果“Flag”值为 False,那么当程序执行 event.wait 方法时就会阻塞,如果“Flag”值为True,那么event.wait 方法时便不再阻塞。
# -*- coding:utf-8 -*- # @Author : Clint import threading def do(event): print('start') event.wait() print('execute') event_obj = threading.Event() for i in range(10): t = threading.Thread(target=do, args=(event_obj,)) t.start() event_obj.clear() inp = input('input:') if inp == 'true': event_obj.set()
Condition(条件)
使得线程等待,只有满足某条件时,才释放n个线程
def condition_func(): ret = False inp = input('>>>') if inp == '1': ret = True return ret def run(n): con.acquire() con.wait_for(condition_func) print("run the thread: %s" %n) con.release() if __name__ == '__main__': con = threading.Condition() for i in range(10): t = threading.Thread(target=run, args=(i,)) t.start()
Timer(定时器)
from threading import Timer def hello(): print("hello, world") t = Timer(1, hello) t.start() # 一秒后打印
进程池
进程池内部维护一个进程序列,当使用时,则去进程池中获取一个进程,如果进程池序列中没有可供使用的进进程,那么程序就会等待,直到进程池中有可用进程为止。
进程池中有两个方法:apply、apply_async
# -*- coding:utf-8 -*- # @Author : Clint from multiprocessing import Process, Pool import time def Foo(i): time.sleep(2) return i + 100 def Bar(arg): print(arg) pool = Pool(5) # print pool.apply(Foo,(1,)) # print pool.apply_async(func =Foo, args=(1,)).get() for i in range(10): pool.apply_async(func=Foo, args=(i,), callback=Bar) print('end') pool.close() pool.join() # 进程池中进程执行完毕后再关闭,如果注释,那么程序直接关闭。