开锁做网站怎么样,西安seo外包,小公司企业简介怎么写,网页上传和网站开发2. 多任务编程
2.1 多任务概述 多任务 即操作系统中可以同时运行多个任务。比如我们可以同时挂着qq#xff0c;听音乐#xff0c;同时上网浏览网页。这是我们看得到的任务#xff0c;在系统中还有很多系统任务在执行,现在的操作系统基本都是多任务操作系统#xff0c;具备…2. 多任务编程
2.1 多任务概述 多任务 即操作系统中可以同时运行多个任务。比如我们可以同时挂着qq听音乐同时上网浏览网页。这是我们看得到的任务在系统中还有很多系统任务在执行,现在的操作系统基本都是多任务操作系统具备运行多任务的能力。 计算机原理 CPU计算机硬件的核心部件用于对任务进行执行运算。 操作系统调用CPU执行任务 cpu轮询机制 cpu都在多个任务之间快速的切换执行切换速度在微秒级别其实cpu同时只执行一个任务但是因为切换太快了从应用层看好像所有任务同时在执行。 多核CPU现在的计算机一般都是多核CPU比如四核八核我们可以理解为由多个单核CPU的集合。这时候在执行任务时就有了选择可以将多个任务分配给某一个cpu核心也可以将多个任务分配给多个cpu核心操作系统会自动根据任务的复杂程度选择最优的分配方案。 并发 多个任务如果被分配给了一个cpu内核那么这多个任务之间就是并发关系并发关系的多个任务之间并不是真正的同时。并行 多个任务如果被分配给了不同的cpu内核那么这多个任务之间执行时就是并行关系并行关系的多个任务时真正的“同时”执行。 什么是多任务编程 多任务编程即一个程序中编写多个任务在程序运行时让这多个任务一起运行而不是一个一个的顺次执行。 比如微信视频聊天这时候在微信运行过程中既用到了视频任务也用到了音频任务甚至同时还能发消息。这就是典型的多任务。而实际的开发过程中这样的情况比比皆是。 实现多任务编程的方法 多进程编程多线程编程 多任务意义 提高了任务之间的配合可以根据运行情况进行任务创建。 比如 你也不知道用户在微信使用中是否会进行视频聊天总不能提前启动起来吧这是需要根据用户的行为启动新任务。 充分利用计算机资源提高了任务的执行效率。 在任务中无阻塞时只有并行状态才能提高效率 在任务中有阻塞时并行并发都能提高效率
2.2 进程Process
2.2.1 进程概述 定义 程序在计算机中的一次执行过程。 程序是一个可执行的文件是静态的占有磁盘。 进程是一个动态的过程描述占有计算机运行资源有一定的生命周期。 进程状态 三态 就绪态 进程具备执行条件等待系统调度分配cpu资源 运行态 进程占有cpu正在运行 等待态 进程阻塞等待此时会让出cpu五态 (在三态基础上增加新建和终止) 新建 创建一个进程获取资源的过程 终止 进程结束释放资源的过程进程命令 查看进程信息 ps -auxUSER 进程的创建者PID : 操作系统分配给进程的编号,大于0的整数系统中每个进程的PID都不重复。PID也是重要的区分进程的标志。%CPU,%MEM : 占有的CPU和内存STAT 进程状态信息S I 表示阻塞状态 R 表示就绪状态或者运行状态START : 进程启动时间COMMAND : 通过什么程序启动的进程 进程树形结构 pstree父子进程在Linux操作系统中进程形成树形关系任务上一级进程是下一级的父进程下一级进程是上一级的子进程。
2.2.2 多进程编程 使用模块 multiprocessing 创建流程 【1】 将需要新进程执行的事件封装为函数 【2】 通过模块的Process类创建进程对象关联函数 【3】 通过进程对象调用start启动进程 主要类和函数使用 Process()功能 创建进程对象参数 target 绑定要执行的目标函数 args 元组用于给target函数位置传参kwargs 字典给target函数键值传参daemon bool值让子进程随父进程退出p.start()功能 启动进程注意 : 启动进程此时target绑定函数开始执行该函数作为新进程执行内容此时进程真正被创建 p.join([timeout])功能阻塞等待子进程退出参数最长等待时间进程创建示例进程创建示例 01import multiprocessing as mp
from time import sleepa 1 # 全局变量# 进程目标函数
def fun():print(开始运行一个进程)sleep(4) # 模拟事件执行事件global aprint(a ,a) # Yesa 10000print(进程执行结束)# 实例化进程对象
process mp.Process(targetfun)# 启动进程 进程产生 执行fun
process.start()print(我也做点事情)
sleep(3)
print(我也把事情做完了...)process.join() # 阻塞等待子进程结束
print(a:,a) # 1 10000进程创建示例02 含有参数的进程函数from multiprocessing import Process
from time import sleep# 含有参数的进程函数
def worker(sec,name):for i in range(3):sleep(sec)print(Im %s%name)print(Im working....)# 元组位置传参
# p Process(targetworker,args(2,Tom))# 关键字传参
p Process(targetworker,args (2,),kwargs{name:Tom},daemonTrue) # 子进程伴随父进程结束
p.start()
sleep(3) 进程执行现象理解 难点 新的进程是原有进程的子进程子进程复制父进程全部内存空间代码段一个进程可以创建多个子进程。子进程只执行指定的函数其余内容均是父进程执行内容但是子进程也拥有其他父进程资源。各个进程在执行上互不影响也没有先后顺序关系。进程创建后各个进程空间独立相互没有影响。multiprocessing 创建的子进程中无法使用标准输入即无法使用input。
2.2.3 进程处理细节
进程相关函数 os.getpid()功能 获取一个进程的PID值返回值 返回当前进程的PID os.getppid()功能 获取父进程的PID号返回值 返回父进程PIDsys.exit(info)功能退出进程参数字符串 表示退出时打印内容创建多个子进程from multiprocessing import Process
from time import sleep
import sys, osdef th1():sleep(3)print(吃饭)print(os.getppid(), --, os.getpid())def th2():# sys.exit(不能睡觉了) # 进程结束sleep(1)print(睡觉)print(os.getppid(), --, os.getpid())def th3():sleep(2)print(打豆豆)print(os.getppid(), --, os.getpid())# 循环创建子进程
jobs [] # 存放每个进程对象
for th in [th1, th2, th3]:p Process(targetth)jobs.append(p) # 存入jobsp.start()# 确保三件事都结束
for i in jobs:i.join()
print(三件事完成)随堂练习大文件拆分
有一个大文件将其拆分成上下两个部分 按照字节大小要求两个部分拆分要同步进行
plus 假设文件很大不要一次read读取全部提示 os.path.getsize() 获取文件大小创建两个子进程分别拆上下两个部分import os
from multiprocessing import Processfilename ./dict.txt
size os.path.getsize(filename)# 如果父进程打开子进程直接用则会公用一个文件偏移量
# fr open(filename,rb)def top():fr open(filename,rb)fw open(top.txt,wb)n size // 2while n 1024:fw.write(fr.read(1024))n - 1024else:fw.write(fr.read(n))fr.close()fw.close()def bot():fr open(filename, rb)fw open(bot.txt, wb)fr.seek(size//2,0) # 文件偏移量到中间while True:data fr.read(1024)if not data:breakfw.write(data)fr.close()fw.close()jobs[]
for item in [top,bot]:p Process(targetitem)jobs.append(p)p.start()[i.join() for i in jobs]
print(拆分完成) 孤儿进程和僵尸进程 孤儿进程 父进程先于子进程退出时子进程会成为孤儿进程孤儿进程会被系统自动收养成为孤儿进程新的父进程并在孤儿进程退出时释放其资源。 僵尸进程 子进程先于父进程退出父进程又没有处理子进程的退出状态此时子进程就会成为僵尸进程。 特点 僵尸进程虽然结束但是会存留部分进程资源在内存中大量的僵尸进程会浪费系统资源。Python模块当中自动建立了僵尸处理机制每次创建新进程都进行检查将之前产生的僵尸处理掉而且父进程退出前僵尸也会被自动处理。
2.2.4 创建进程类
进程的基本创建方法将子进程执行的内容封装为函数。如果我们更热衷于面向对象的编程思想也可以使用类来封装进程内容。 创建步骤 【1】 继承Process类 【2】 重写__init__方法添加自己的属性使用super()加载父类属性 【3】 重写run()方法 使用方法 【1】 实例化对象 【2】 调用start自动执行run方法
自定义进程类from multiprocessing import Process
from time import sleepclass MyProcess(Process):def __init__(self, value):self.value valuesuper().__init__() # 调用父类的init# 重写run 作为进程的执行内容def run(self):for i in range(self.value):sleep(2)print(自定义进程类。。。。)p MyProcess(3)
p.start() # 将 run方法作为进程执行随堂练习
1. 求100000以内质数之和并且计算这个求和过程的时间2. 将100000分成4份创建4个进程每个进程求其中一份的
质数之和统计4个进程执行完的时间提示
质数: 只能被1和其本身整除的整数 1
import time time.time()import time
from multiprocessing import Process# 求begin -- end 之间的质数之和
class Prime(Process):staticmethoddef is_prime(n):if n 1:return Falsefor i in range(2, n // 2 1):if n % i 0:return Falsereturn Truedef __init__(self,begin,end):self.begin begin # 起始数字self.end end # 结尾数字super().__init__()def run(self):prime [] # 存放所有质数for i in range(self.begin,self.end):if Prime.is_prime(i):prime.append(i) # 存入列表print(sum(prime))if __name__ __main__:# 4进程 用时: 9.434056282043457# 10进程 用时: 8.70681643486023jobs []b time.time()for i in range(1,100001,10000):p Prime(i,i 10000)jobs.append(p)p.start()[i.join() for i in jobs]print(用时:,time.time()-b)# def is_prime(n):
# if n 1:
# return False
# for i in range(2,n // 2 1):
# if n % i 0:
# return False
# return True# def prime_sum():
# prime [] # 存放所有质数
# for i in range(100001):
# if is_prime(i):
# prime.append(i) # 存入列表
# print(sum(prime))# 用时 16.357502222061157
# begin time.time()
# prime_sum()
# print(用时,time.time()-begin)2.2.5 进程间通信 必要性 进程间空间独立资源不共享此时在需要进程间数据传输时就需要特定的手段进行数据通信。 常用进程间通信方法消息队列套接字等。 消息队列使用 通信原理 在内存中开辟空间建立队列模型进程通过队列将消息存入或者从队列取出完成进程间通信。实现方法 from multiprocessing import Queueq Queue(maxsize0)
功能: 创建队列对象
参数最多存放消息个数
返回值队列对象q.put(data)
功能向队列存入消息
参数data 要存入的内容q.get()
功能从队列取出消息
返回值 返回获取到的内容q.full() 判断队列是否为满
q.empty() 判断队列是否为空
q.qsize() 获取队列中消息个数
q.close() 关闭队列进程间通信示例
from multiprocessing import Process,Queue# 创建消息队列
q Queue(5)# 子进程函数
def handle():while True:cmd q.get() # 取出指令if cmd 1:print(\n完成指令1)elif cmd 2:print(\n完成指令2)# 创建进程
p Process(targethandle,daemonTrue)
p.start()while True:cmd input(指令)if not cmd:breakq.put(cmd) # 通过队列给子进程
随堂练习
有一个目录中有若干普通文件将该目录复制一份到当前程序所在位置
要求 目标文件夹中每个文件复制都采用一个独立的进程完成当所有文件复制完成之后按复制完成顺序打印所有文件名
提示
创建文件夹 os.mkdir(dir) os.listdir()from multiprocessing import Process, Queue
import os# 消息队列
q Queue()# 进程函数 复制文件
def copy(old,new,file):fr open(old/file,rb)fw open(new/file,wb)while True:data fr.read()if not data:breakfw.write(data)fr.close()fw.close()q.put(file) # 存入队列# 入口函数
def main(old):new old.split(/)[-1]os.mkdir(new) # 创建新文件夹jobs [] # 存放每个进程对象# 循环创建进程 file -- 文件名称for file in os.listdir(old):p Process(targetcopy,args(old,new,file))jobs.append(p)p.start()[i.join() for i in jobs] # 判断所有进程结束print(拷贝了如下文件:)while q.qsize():print(q.get())if __name__ __main__:main(/home/tarena/FTP)
**群聊聊天室 ** 功能 类似qq群功能 【1】 有人进入聊天室需要输入姓名姓名不能重复 【2】 有人进入聊天室时其他人会收到通知Lucy 进入了聊天室 【3】 一个人发消息其他人会收到 Lucy 一起出去玩啊。 【4】 有人退出聊天室则其他人也会收到通知 : Lucy 退出了聊天室 【5】 扩展功能服务器可以向所有用户发送公告: 管理员消息 大家好欢迎进入聊天室。 ################ 服务端参考代码 ###################
from socket import *
from multiprocessing import Process# 服务器地址
HOST 0.0.0.0
PORT 8888
ADDR (HOST, PORT)# 存储用户信息 {name:address}
user {}# 处理进入聊天室
def login(sock, name, address):if name in user or 管理 in name:sock.sendto(bFAIL, address)else:sock.sendto(bOK, address)# 告知其他人msg 欢迎 %s 进入聊天室 % namefor key, value in user.items():sock.sendto(msg.encode(), value)user[name] address # 存储用户# print(user) # 测试# 处理聊天
def chat(sock, name, content):msg %s : %s % (name, content)for key, value in user.items():# 不是本人就发送if key ! name:sock.sendto(msg.encode(), value)# 处理退出
def exit(sock, name):if name in user:del user[name] # 删除该用户# 通知其他用户msg %s 退出聊天室 % namefor key, value in user.items():sock.sendto(msg.encode(), value)def handle(sock):# 不断接收请求分情况讨论while True:request, addr sock.recvfrom(1024)tmp request.decode().split( , 2)# 分情况讨论if tmp[0] LOGIN:# tmp -[LOGIN,name]login(sock, tmp[1], addr)elif tmp[0] CHAT:# tmp -[CHAT,name,content]chat(sock, tmp[1], tmp[2])elif tmp[0] EXIT:# tmp -[EXIT,name]exit(sock, tmp[1])# 程序入口函数
def main():# 创建udpsock socket(AF_INET, SOCK_DGRAM)sock.bind(ADDR)# 接收请求分类处理p Process(targethandle, args(sock,), daemonTrue)p.start()while True:content input(管理员消息:)if not content:breakmsg CHAT 管理员消息 content# 从父进程发送到子进程sock.sendto(msg.encode(), ADDR)if __name__ __main__:main()################## 客户端参考代码 ##################
from socket import *
from multiprocessing import Process
import sys# 服务器地址
SERVER_ADDR (XX.XX.XXX.XX, 8888)def login(sock):while True:name input(请输入昵称:)# 组织请求msg LOGIN namesock.sendto(msg.encode(), SERVER_ADDR)result, addr sock.recvfrom(1024)if result bOK:print(进入聊天室)return nameelse:print(该昵称已存在)# 子进程接收函数
def recv_msg(sock):while True:data, addr sock.recvfrom(1024 * 10)# 格式处理content \n data.decode() \n发言print(content, end)# 父进程发送函数
def send_msg(sock, name):while True:try:content input(发言)except KeyboardInterrupt:content exit# 表示退出if content exit:msg EXIT namesock.sendto(msg.encode(), SERVER_ADDR)sys.exit(您已退出聊天室)msg CHAT %s %s % (name, content)sock.sendto(msg.encode(), SERVER_ADDR)def main():sock socket(AF_INET, SOCK_DGRAM)sock.bind((0.0.0.0,55224)) # 端口不要变name login(sock) # 请求进入聊天室# 子进程负责接收p Process(targetrecv_msg, args(sock,), daemonTrue)p.start()send_msg(sock, name) # 发送消息if __name__ __main__:main()
2.3 线程 (Thread)
2.3.1 线程概述 什么是线程 【1】 线程被称为轻量级的进程也是多任务编程方式 【2】 也可以利用计算机的多cpu资源 【3】 线程可以理解为进程中再开辟的分支任务 线程特征 【1】 一个进程中可以包含多个线程 【2】 线程也是一个运行行为消耗计算机资源 【3】 一个进程中的所有线程共享这个进程的资源 【4】 多个线程之间的运行同样互不影响各自运行 【5】 线程的创建和销毁消耗资源远小于进程
2.3.2 多线程编程
线程模块 threading 创建方法 【1】 创建线程对象
from threading import Thread t Thread()
功能创建线程对象
参数target 绑定线程函数args 元组 给线程函数位置传参kwargs 字典 给线程函数键值传参daemon bool值主线程推出时该分支线程也推出【2】 启动线程t.start()【3】等待分支线程结束t.join([timeout])
功能阻塞等待分支线程退出
参数最长等待时间线程示例01import threading
from time import sleep
import osa 1# 线程函数
def music():global aprint(a ,a)a 10000for i in range(3):sleep(2)print(os.getpid(),播放:黄河大合唱)# 实例化线程对象
thread threading.Thread(targetmusic)
# 启动线程 线程存在
thread.start()for i in range(4):sleep(1)print(os.getpid(),播放:葫芦娃)# 阻塞等待分支线程结束
thread.join()
print(a:,a)线程示例02from threading import Thread
from time import sleep# 带有参数的线程函数
def func(sec,name):print(含有参数的线程来喽)sleep(sec)print(%s 线程执行完毕%name)# 循环创建线程
for i in range(5):t Thread(targetfunc,args(2,),kwargs{name:T-%d%i},daemonTrue)t.start()2.3.3 创建线程类 创建步骤 【1】 继承Thread类 【2】 重写__init__方法添加自己的属性使用super()加载父类属性 【3】 重写run()方法 使用方法 【1】 实例化对象 【2】 调用start自动执行run方法 from threading import Thread
from time import sleepclass MyThread(Thread):def __init__(self,song):self.song songsuper().__init__() # 得到父类内容# 线程要做的事情def run(self):for i in range(3):sleep(2)print(播放:,self.song)t MyThread(凉凉)
t.start() # 运行run随堂练习
现在有500张票存在一个列表中 [T1,....T500]10个窗口同时卖这500张票 W1-W10使用10个线程模拟这10个窗口同时卖票直到所有的票都卖出为止每出一张票 需要0.1秒打印表示即可print(W1----T250)from threading import Thread,Lock
from time import sleeplock Lock() # 创建锁# 将票准备好
ticket [T%d % x for x in range(1, 501)]# 线程函数 w:表示窗口
def sell(w):while ticket:print(%s --- %s%(w,ticket.pop(0)))sleep(0.1)# 10个线程
for i in range(1,11):t Thread(targetsell,args(W%d%i,))t.start()2.3.4 线程同步互斥 线程通信方法 线程间使用全局变量进行通信 共享资源争夺 共享资源多线程都可以操作的资源称为共享资源。对共享资源的操作代码段称为临界区。影响 对共享资源的无序操作可能会带来数据的混乱或者操作错误。此时往往需要同步互斥机制协调操作顺序。 同步互斥机制 同步 同步是一种协作关系为完成操作线程间形成一种协调按照必要的步骤有序执行操作。 互斥 互斥是一种制约关系当一个进程或者线程占有资源时会进行加锁处理此时其他进程线程就无法操作该资源直到解锁后才能操作。 线程Event
from threading import Evente Event() 创建线程event对象e.wait([timeout]) 阻塞等待e被sete.set() 设置e使wait结束阻塞e.clear() 使e回到未被设置状态e.is_set() 查看当前e是否被设置Event使用示例from threading import Thread, Eventmsg None # 通信变量
e Event() # 事件对象def 杨子荣():print(杨子荣前来拜山头)global msgmsg 天王盖地虎e.set() # 通知主线程可以判断t Thread(target杨子荣)
t.start()print(说对口令才是自己人)
e.wait() # 阻塞等待通知
if msg 天王盖地虎:print(宝塔镇河妖)print(确认过眼神你是对的人)
else:print(打死他.... 无情啊 哥哥....)
线程锁 Lock
from threading import Locklock Lock() 创建锁对象
lock.acquire() 上锁 如果lock已经上锁再调用会阻塞
lock.release() 解锁Lock使用示例from threading import Thread, Locklock Lock() # 创建锁
a b 0def value():while True:lock.acquire() # 上锁if a ! b:print(a %d,b %d % (a, b))lock.release() # 解锁t Thread(targetvalue)
t.start()while True:lock.acquire()a 1b 1lock.release()
随堂练习
使用两个分支线程一个线程打印1-52 这52个数字另一个线程打印A-Z 这26个字母。要求同时执行两个线程打印顺序为 12A34B....5152Zfrom threading import Thread,Locklock1 Lock()
lock2 Lock()def print_num():for i in range(1,53,2):lock1.acquire()print(i)print(i 1)lock2.release()def print_chr():for i in range(65,91):lock2.acquire()print(chr(i))lock1.release()t1 Thread(targetprint_num)
t2 Thread(targetprint_chr)lock2.acquire() # 先把打印字母的部分锁住t1.start()
t2.start()2.3.5 死锁 什么是死锁 死锁是指两个或两个以上的线程在执行过程中由于竞争资源或者由于彼此通信而造成的一种阻塞的现象若无外力作用它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁。 死锁产生条件 互斥条件指线程使用了互斥方法使用一个资源时其他线程无法使用。 请求和保持条件指线程已经保持至少一个资源但又提出了新的资源请求在获取到新的资源前不会释放自己保持的资源。 不剥夺条件不会受到线程外部的干扰如系统强制终止线程等。 环路等待条件指在发生死锁时必然存在一个线程——资源的环形链如 T0正在等待一个T1占用的资源T1正在等待T2占用的资源……Tn正在等待已被T0占用的资源。 如何避免死锁 逻辑清晰不要同时出现上述死锁产生的四个条件通过测试工程师进行死锁检测
死锁现象演示
死锁现象演示from time import sleep
from threading import Thread,Lock# 账户类
class Account:def __init__(self,id,balance,lock):self._id idself._balance balanceself.lock lock# 取钱def withdraw(self,amount):self._balance - amount# 存钱def deposit(self,amount):self._balance amount# 查看余额def getBalance(self):return self._balance# 转账函数
def transfer(from_,to,amount):from_.lock.acquire()from_.withdraw(amount) # from_钱减少from_.lock.release() # 不会产生死锁sleep(0.1) # 网络延迟to.lock.acquire()to.deposit(amount) # to钱增加# from_.lock.release() # 产生死锁to.lock.release()if __name__ __main__:tom Account(Tom,5000,Lock())abby Account(abby,8000,Lock())t1 Thread(targettransfer,args(tom,abby,2000))t2 Thread(targettransfer,args(abby,tom,3000))t1.start()t2.start()t1.join()t2.join()print(Tom:,tom.getBalance())print(Abby:,abby.getBalance())
2.3.6 GIL问题 什么是GIL问题 全局解释器锁 由于python解释器设计中加入了解释器锁导致python解释器同一时刻只能解释执行一个线程大大降低了线程的执行效率。 导致后果 因为遇到阻塞时线程会主动让出解释器去解释其他线程。所以python多线程在执行多阻塞任务时可以提升程序效率其他情况并不能对效率有所提升。 关于GIL问题的处理 尽量使用进程完成无阻塞的并发行为 不使用c作为解释器 可以用Java C# Guido的声明http://www.artima.com/forums/flat.jsp?forum106thread214235 结论 GIL问题与Python语言本身并没什么关系属于解释器设计的历史问题。在无阻塞状态下多线程程序程序执行效率并不高甚至还不如单线程效率。Python多线程只适用于执行有阻塞延迟的任务情形。
线程效率对比进程实验class Prime(Thread):# 判断一个数是否为质数staticmethoddef is_prime(n):if n 1:return Falsefor i in range(2,n // 2 1):if n % i 0:return Falsereturn Truedef __init__(self,begin,end):self.__begin beginself.__end endsuper().__init__()def run(self):prime [] # 存放所有质数for i in range(self.__begin,self.__end):if Prime.is_prime(i):prime.append(i)print(sum(prime))timeis
def process_10():jobs []for i in range(1,100001,10000):t Prime(i,i 10000)jobs.append(t)t.start()for i in jobs:i.join()if __name__ __main__:process_10()2.3.7 进程线程的区别联系
区别联系
两者都是多任务编程方式都能使用计算机多核资源进程的创建删除消耗的计算机资源比线程多进程空间独立数据互不干扰有专门通信方法线程使用全局变量通信一个进程可以有多个分支线程两者有包含关系多个线程共享进程资源在共享资源操作时往往需要同步互斥处理Python线程存在GIL问题但是进程没有。 使用场景
任务场景一个大型服务往往包含多个独立的任务模块每个任务模块又有多个小独立任务构成此时整个项目可能有多个进程每个进程又有多个线程。编程语言Java,C#之类的编程语言在执行多任务时一般都是用线程完成因为线程资源消耗少而Python由于GIL问题往往使用多进程。