操作系统/应用程序/操作系统中的并发/线程与进程(Python中有GIL锁)
一.操作系统/应用程序
1.硬件:
2.操作系统:
3.应用程序:
(1).应用程式是电脑软体的主要分类之一,是指为针对使用者的某种应用目的所撰写的软体。
(2).应用程式通常又被分为两部分:图形使用者介面(GUI)和引擎 (Engine)。它与应用软体的概念不同。应用软体指使用的目的分类,可以是单一程式或其他从属元件的集合,例如Microsoft Office、OpenOffice。应用程式指单一可执行档或单一程式,例如Word、Photoshop。日常中可不将两者仔细区分。一般视程式为软体的一个组成部分。
二.操作系统中的并发与并行
2.并发:并发是指资源有限的情况下,两者交替轮流使用资源,比如只有一条车道(单核CPU资源),那么就是A车先走,在某个时刻A车退出把道路让给B走,B走完继续给A ,交替使用,目的是提高效率。
3.区别:
(1).并行是从微观上,也就是在一个精确的时间片刻,有不同的程序在执行,这就要求必须有多个处理器。
(2).并发是从宏观上,在一个时间段上可以看出是同时执行的,比如一个服务器同时处理多个session。
注意:早期单核CPU时候,对于进程也是微观上串行(站在cpu角度看),宏观上并行(站在人的角度看就是同时有很多程序在执行)。
4.同步:所谓同步就是一个任务的完成需要依赖另外一个任务时,只有等待被依赖的任务完成后,依赖的任务才能算完成,这是一种可靠的任务序列。要么成功都成功,失败都失败,两个任务的状态可以保持一致。
5.异步:所谓异步是不需要等待被依赖的任务完成,只是通知被依赖的任务要完成什么工作,依赖的任务也立即执行,只要自己完成了整个任务就算完成了。至于被依赖的任务最终是否真正完成,依赖它的任务无法确定,所以它是不可靠的任务序列。
6.阻塞与非阻塞
阻塞和非阻塞这两个概念与程序(线程)等待消息通知(无所谓同步或者异步)时的状态有关。也就是说阻塞与非阻塞主要是程序(线程)等待消息通知时的状态角度来说的
三.线程与进程
1、线程的基本概念
(1).概念:
线程是进程中执行运算的最小单位,是进程中的一个实体,是被系统独立调度和分派的基本单位,线程自己不拥有系统资源,只拥有一点在运行中必不可少的资源,但它可与同属一个进程的其它线程共享进程所拥有的全部资源。一个线程可以创建和撤消另一个线程,同一进程中的多个线程之间可以并发执行。
(2)好处:
易于调度。
提高并发性。通过线程可方便有效地实现并发性。进程可创建多个线程来执行同一程序的不同部分。
开销少。创建线程比创建进程要快,所需开销很少
2、进程的基本状态及状态之间的关系
状态:运行、阻塞、挂起阻塞、就绪、挂起就绪
状态之间的转换:
(1)准备就绪的进程,被CPU调度执行,变成运行态;
(2)运行中的进程,进行I/O请求或者不能得到所请求的资源,变成阻塞态;
(3)运行中的进程,进程执行完毕(或时间片已到),变成就绪态;
(4)将阻塞态的进程挂起,变成挂起阻塞态,当导致进程阻塞的I/O操作在用户重启进程前完成(称之为唤醒),挂起阻塞态变成挂起就绪态,当用户在I/O操作结束之前重启进程,挂起阻塞态变成阻塞态;
(5)将就绪(或运行)中的进程挂起,变成挂起就绪态,当该进程恢复之后,挂起就绪态变成就绪态;
3、线程和进程的关系以及区别?
进程和线程的关系:
(1)一个线程只能属于一个进程,而一个进程可以有多个线程,但至少有一个线程。
(2)资源分配给进程,同一进程的所有线程共享该进程的所有资源。
(3)处理机分给线程,即真正在处理机上运行的是线程
(4)线程在执行过程中,需要协作同步。不同进程的线程间要利用消息通信的办法实现同步。线程是指进程内的一个执行单元,也是进程内的可调度实体.
进程与线程的区别
(1)调度:线程作为调度和分配的基本单位,进程作为拥有资源的基本单位
(2)并发性:不仅进程之间可以并发执行,同一个进程的多个线程之间也可并发执行
(3)拥有资源:进程是拥有资源的一个独立单位,线程不拥有系统资源,但可以访问隶属于进程的资源.
(4)系统开销:在创建或撤消进程时,由于系统都要为之分配和回收资源,导致系统的开销明显大于创建或撤消线程时的开销。

print('666')

import threading print('666') def func(arg): print(arg) t = threading.Thread(target=func) t.start() print('end')
4.线程的使用
(1).Threading模块:
Threading用于提供线程相关的操作,线程是应用程序中工作的最小单元
(2).更多方法:
start 线程准备就绪,等待CPU调度
setName 为线程设置名称
getName 获取线程名称
setDaemon 设置为后台线程或前台线程(默认) 守护进程:setDaemon=False
如果是后台线程,主线程执行过程中,后台线程也在进行,主线程执行完毕后,后台线程
不论成功与否,均停止
如果是前台线程,主线程执行过程中,前台线程也在进行,主线程执行完毕后,等待前台
线程也执行完成后,程序停止
join 逐个执行每个线程,执行完毕后继续往下执行,该方法使得多线程变得无意义
run 线程被cpu调度后自动执行线程对象的run方法

def func(arg): print(arg) t = threading.Thread(target=func,args=(11,)) t.start() print(123) 结果: 11 123 end

import time def func(arg): time.sleep(arg) print(arg) t1 = threading.Thread(target=func,args=(3,)) t1.start() t2 = threading.Thread(target=func,args=(9,)) t2.start() print(123) 结果: 123 end ...等一会 3 9

import time def func(arg): time.sleep(2) print(arg) t1 = threading.Thread(target=func,args=(3,)) t1.setDaemon(True) t1.start() t2 = threading.Thread(target=func,args=(9,)) t2.setDaemon(True) t2.start() print(123) 结果: 123 end

import time def func(arg): time.sleep(0.01) print(arg) print('创建子线程t1') t1 = threading.Thread(target=func,args=(3,)) t1.start() # 无参数,让主线程在这里等着,等到子线程t1执行完毕,才可以继续往下走。 # 有参数,让主线程在这里最多等待n秒,无论是否执行完毕,会继续往下走。 t1.join(2) print('创建子线程t2') t2 = threading.Thread(target=func,args=(9,)) t2.start() t2.join(2) # 让主线程在这里等着,等到子线程t2执行完毕,才可以继续往下走。 print(123) 结果: 123 创建子线程t1 3 创建子线程t2 9 123 end

# 先打印:11?123? def func(arg): print(arg) t1 = threading.Thread(target=func,args=(11,)) t1.start() # start 是开始运行线程吗?不是 # start 告诉cpu,我已经准备就绪,你可以调度我了。 print(123) 结果: 123 11 123 end

# 多线程方式:1 (常见) def func(arg): print(arg) t1 = threading.Thread(target=func,args=(11,)) t1.start() 结果: 123 11 # 多线程方式:2 class MyThread(threading.Thread): def run(self): print(11111,self._args,self._kwargs) t1 = MyThread(args=(11,)) t1.start() t2 = MyThread(args=(22,)) t2.start() print('end') 结果: 123 11111 (11,) {} 11111 (22,) {} end
5.Python多线的情况下:
计算密集型操作:效率低.(GIL锁)
IO操作:效率高
6.Python多进程的情况下:
计算密集型操作:效率高(浪费资源). 不得已而为之
IO操作:效率高(浪费资源)
7.以后写Python时:
IO密集型用多线程:文件/输入输出/socket网络通信
计算密集型用多线程
8.扩展
JAVA多线程的情况下:
计算密集型操作:效率高
IO操作:效率高
Python多进程的情况下:
计算密集型操作:效率高(浪费资源)
IO操作:效率高(浪费资源)
四.Python中线程和进程(GIL锁)
GIL锁:全局解释器锁,用于限制一个进程中同一时刻只有一个线程被CPU调用
扩展:默认GIL锁在执行100个CPU指令(过期时间)

import sys v1 = sys.getcheckinterval() print(v1) 结果:100
1.Python线程的编写
(1).计算密集型多线程无用

import threading v1 = [11,22,33] # +1 v2 = [44,55,66] # 100 def func(data,plus): for i in range(len(data)): data[i] = data[i] + plus print(data) t1 = threading.Thread(target=func,args=(v1,1)) t1.start() t2 = threading.Thread(target=func,args=(v2,100)) t2.start() 结果: [12, 23, 34] [144, 155, 166]
(2).IO操作,多线程有用

import threading import requests import uuid url_list = [ 'https://www3.autoimg.cn/newsdfs/g28/M05/F9/98/120x90_0_autohomecar__ChsEnluQmUmARAhAAAFES6mpmTM281.jpg', 'https://www2.autoimg.cn/newsdfs/g28/M09/FC/06/120x90_0_autohomecar__ChcCR1uQlD6AT4P3AAGRMJX7834274.jpg', 'https://www2.autoimg.cn/newsdfs/g3/M00/C6/A9/120x90_0_autohomecar__ChsEkVuPsdqAQz3zAAEYvWuAspI061.jpg', ] def task(url): ret = requests.get(url) file_name = str(uuid.uuid4()) + '.jpg' with open(file_name, mode='wb') as f: f.write(ret.content) for url in url_list: t = threading.Thread(target=task,args=(url,)) t.start()
(3).多线程的问题

import time import threading lock = threading.RLock() n = 10 def task(i): print('这段代码不加锁',i) lock.acquire() # 加锁,此区域的代码同一时刻只能有一个线程执行 global n print('当前线程',i,'读取到的n值为:',n) n = i time.sleep(1) print('当前线程',i,'修改n值为:',n) lock.release() # 释放锁 for i in range(10): t = threading.Thread(target=task,args=(i,)) t.start() 结果: 这段代码不加锁 0 当前线程 0 读取到的n值为: 10 这段代码不加锁 1 这段代码不加锁 2 这段代码不加锁 3 这段代码不加锁 4 这段代码不加锁 5 这段代码不加锁 6 这段代码不加锁 7 这段代码不加锁 8 这段代码不加锁 9 ...每输出一项,等一秒 当前线程 0 修改n值为: 0 当前线程 1 读取到的n值为: 0 当前线程 1 修改n值为: 1 当前线程 2 读取到的n值为: 1 当前线程 2 修改n值为: 2 当前线程 3 读取到的n值为: 2 当前线程 3 修改n值为: 3 当前线程 4 读取到的n值为: 3 当前线程 4 修改n值为: 4 当前线程 5 读取到的n值为: 4 当前线程 5 修改n值为: 5 当前线程 6 读取到的n值为: 5 当前线程 6 修改n值为: 6 当前线程 7 读取到的n值为: 6 当前线程 7 修改n值为: 7 当前线程 8 读取到的n值为: 7 当前线程 8 修改n值为: 8 当前线程 9 读取到的n值为: 8 当前线程 9 修改n值为: 9