本节内容
一、进程与线程的概念
1.1进程
1.2线程
1.3进程与线程的区别
二、线程
2、1启一个线程
2.2线程的2种调用方式
2.3 join
2.4 守护线程Daemon
2.5线程锁
2.6全局解析锁
2.7递归锁
2.8Semaphore(信号量)
2.9事件Events(红绿灯举例)
2.10queue队列
2.11生产消费者模型
三、进程
3.1多进程
3.2进程间通讯
3.3进程池
1.1进程
什么是进程(process)?
以QQ为例, QQ 要以一个整体的形式暴露给操作系统管理,里面包含对各种资源的调用,内存的管理,网络接口的调用等。。。。对各种资源管理的集合 就可以称为: 进程。
程序的执行实例称为进程。每个进程都提供执行程序所需的资源。 进程具有虚拟地址空间,可执行代码,系统对象的打开句柄,安全上下文,唯一进程标识符,环境变量,优先级类,最小和最大工作集大小以及至少一个执行线程。 每个进程都使用单个线程启动,通常称为主线程,但可以从其任何线程创建其他线程。
程序并不能单独运行,只有将程序装载到内存中,系统为它分配资源才能运行,而这种执行的程序就称之为进程。程序和进程的区别就在于:程序是指令的集合,它是进程运行的静态描述文本;进程是程序的一次执行活动,属于动态概念。在多道编程中,我们允许多个程序同时加载到内存中,在操作系统的调度下,可以实现并发地执行。这是这样的设计,大大提高了CPU的利用率。进程的出现让每个用户感觉到自己独享CPU,因此,进程就是为了在CPU上实现多道编程而提出的。
有了进程为什么还要线程?
进程有很多优点,它提供了多道编程,让我们感觉我们每个人都拥有自己的CPU和其他资源,可以提高计算机的利用率。很多人就不理解了,既然进程这么优秀,为什么还要线程呢?其实,仔细观察就会发现进程还是有很多缺陷的,主要体现在两点上:
-
进程只能在一个时间干一件事,如果想同时干两件事或多件事,进程就无能为力了。
-
进程在执行的过程中如果阻塞,例如等待输入,整个进程就会挂起,即使进程中有些工作不依赖于输入的数据,也将无法执行。
例如,我们在使用qq聊天, qq做为一个独立进程如果同一时间只能干一件事,那他如何实现在同一时刻 即能监听键盘输入、又能监听其它人给你发的消息、同时还能把别人发的消息显示在屏幕上呢?你会说,操作系统不是有分时么?但我的亲,分时是指在不同进程间的分时呀, 即操作系统处理一会你的qq任务,又切换到word文档任务上了,每个cpu时间片分给你的qq程序时,你的qq还是只能同时干一件事呀。
再直白一点, 一个操作系统就像是一个工厂,工厂里面有很多个生产车间,不同的车间生产不同的产品,每个车间就相当于一个进程,且你的工厂又穷,供电不足,同一时间只能给一个车间供电,为了能让所有车间都能同时生产,你的工厂的电工只能给不同的车间分时供电,但是轮到你的qq车间时,发现只有一个干活的工人,结果生产效率极低,为了解决这个问题,应该怎么办呢?。。。。没错,你肯定想到了,就是多加几个工人,让几个人工人并行工作,这每个工人,就是线程!
1.2线程
线程: 是操作系统最小的调度单位, 是一串指令的集合。是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。官网链接:https://docs.python.org/3/library/threading.html?highlight=threading#
线程是执行上下文,它是CPU执行指令流所需的所有信息。
假设你正在读一本书,而你现在想休息一下,但是你希望能够从你停下来的确切位置回来并继续阅读。实现这一目标的一种方法是记下页码,行号和字号。因此,阅读书籍的执行环境就是这三个数字。如果你有一个室友,并且她使用相同的技术,她可以在你不使用时拿走这本书,并从她停下的地方继续阅读。然后你可以把它拿回来,并从你原来的地方恢复。
线程以相同的方式工作。 CPU正在给你一种错觉,即它同时进行多次计算。它通过在每次计算上花费一点时间来做到这一点。它可以这样做,因为它具有每个计算的执行上下文。就像您可以与朋友共享一本书一样,许多任务可以共享CPU。在更技术层面上,执行上下文(因此是一个线程)由CPU寄存器的值组成。最后:线程与进程不同。线程是执行的上下文,而进程是与计算相关联的一堆资源。一个进程可以有一个或多个线程。澄清:与进程相关联的资源包括内存页面(进程中的所有线程具有相同的内存视图),文件描述符(例如,打开套接字)和安全凭证(例如,启动该进程的用户的ID)处理)。
1.3进程与线程的区别:
进程 要操作cpu , 必须要先创建一个线程 ,所有在同一个进程里的线程是共享同一块内存空间的。
1、线程共享创建它的进程的地址空间; 进程有自己的地址空间。(线程共享内存空间,进程的内存是独立的)
2、线程可以直接访问其进程的数据段; 进程拥有自己父进程数据段的副本。
3、线程可以直接与其进程的其他线程通信; 进程必须使用进程间通信来与兄弟进程通信。(同一个进程的线程之间可以直接交流,两个进程想通信,必须通过一个中间代理来实现)
4、创建新线程很简单, 创建新进程需要对其父进程进行一次克隆。
5、一个线程可以控制和操作同一进程里的其他线程,但是进程只能操作子进程。
6、对主线程的更改(取消,优先级更改等)可能会影响进程的其他线程的行为; 对父进程的更改不会影响子进程。
二、线程
2.1启动一个线程
举例子,演示一个最简单的多线程
1 import threading 2 import time 3 4 def run(n): 5 print("task",n) 6 time.sleep(2) 7 8 t1 = threading.Thread(target=run,args=("t1",)) 9 t2 = threading.Thread(target=run,args=("t2",)) 10 #总共运行了2秒,因为是并行的2秒 11 t1.start() 12 t2.start() 13 14 #比较,这里会运行4秒,是先后各两秒 15 # run(t1) 16 # run(t2)
2、2线程的2种调用方式
Python threading模块线程有2种调用方式,如下:
1 import threading 2 import time 3 4 5 def sayhi(num): # 定义每个线程要运行的函数 6 7 print("running on number:%s" % num) 8 9 time.sleep(3) 10 11 12 if __name__ == '__main__': 13 t1 = threading.Thread(target=sayhi, args=(1,)) # 生成一个线程实例 14 t2 = threading.Thread(target=sayhi, args=(2,)) # 生成另一个线程实例 15 16 t1.start() # 启动线程 17 t2.start() # 启动另一个线程 18 19 print(t1.getName()) # 获取线程名 20 print(t2.getName())