第五周学习综述
定义函数,该函数为子进程要执行的内容,紧接着创建if __name__=='__main__':,主进程开始进行,用空列表来储存子进程,在遍历循环中,p=Process(target=test)创建子进程,并在循环中启动子进程,将子进程添加到列表中,其中遍历列表中的五个子进程,遍历阻塞主进程,每个子进程都要监听到阻塞行为才能确保主进程成功阻塞。学习了进程与线程这一模块,知道了进程,线程,程序,进程
本周首先学习了文件中的高维数据的存储和读取,os模块中的函数以及os.path子模块中的常用函数。认识了网络编程中的网络结构,TCP,IP,IP地址,端口,UDP协议,socket类,TCP/IP协议中的四个层次,TCP/IP协议数据发送和数据接收,TCP协议的三次握手以及Socket对象的常用方法。学习了进程与线程这一模块,知道了进程,线程,程序,进程池,并发,并行,队列等基础知识。
一.文件
1.高维数据的存储与读取
通过将高维数据转为二维数据并将其编码到文件中
import json
lst=[
{'name':'ysj','age':'20','height':'168'},
{'name':'lcx','age':'19','height':'170'},
{'name':'sym','age':'18','height':'165'}
]
#编码
s=json.dumps(lst,ensure_ascii=False,indent=4)#ensure_ascii=False防止中文乱码,indent增加数据的缩进
print(type(s),s)#str类型
#解码
s2=json.loads(s)
print(type(s2),s2)#lst类型
#编码到文件中
with open("test.txt",'w',encoding='utf-8') as f:
json.dump(lst,f,ensure_ascii=False,indent=4)

2.os模块中的常用函数
从os模块中学习了如何获取当前工作路径os.getcwd(),当前路径下的所有目录及文lst=os.listdir()如何指定路径下所有目录及文件,创建目录os.mkdir(),创建多级目录os.makedirs(),删除目录os.ramdir(),删除多级目录os.removedirs(),改变当前的工作路径os.chdir()改写后再写代码更换工作路径,获取文件信息os.stat(),最近一次访问时间date_format(info.st_atime),该文件创建时间date_format(info.st_ctime),最后一次修改时间date_format(info.st_mtime),文件的大小(单位是字节)date_format(info.st_size),其中date_format函数接受时间戳longtime格式化为可读的时间字符串,info.st_atime调用该函数,启动路径下的文件os.startfile()
import os
print('获取当前工作路径',os.getcwd())
lst=os.listdir()
print('当前路径下的所有目录及文件',lst)#以列表形式呈现
print('指定路径下所有目录及文件',os.listdir('D:\python\Lib\site-packages'))#并不详细
#创建目录
#os.mkdir('好好学习')#如果存在报错
#创建多级目录
#os.makedirs('./aa/bb/cc')
#删除目录
#os.rmdir('好好学习')
#删除多级目录
#os.removedirs('./aa/bb/cc')#一定与创建的多级目录相同
#改变当前工作路径
os.chdir('D:\python\Lib\site-packages\文件')
print('当前的工作路径',os.getcwd())#再写代码,工作路径就是D:\python
#遍历目录树
for dirs,dirlst,filelst in os.walk('D:\python'):
print(dirs)#文件地址
print(dirlst)#文件名
print(filelst)#文件目录
#os模块高级操作
#os.remove('a.txt')#删除文件
#os.rename('./dd.txt','./aa.txt')#重命名,要与路径相匹配
#转换时间格式
import time
def date_format(longtime):
s=time.strftime('%Y-%m-%d %H:%M:%S',time.localtime(longtime))
return s
#获取文件信息
info=os.stat('./aa.txt')
print(info)
print('最近一次访问时间:',date_format(info.st_atime)) #date_format函数接受时间戳longtime格式化为可读的时间字符串,info.st_atime调用该函数
#调用前需要进行文件获取信息
print('该文件创建时间:',date_format(info.st_ctime))
print('最后一次修改时间:',date_format(info.st_mtime))
print('文件的大小(单位是字节):',date_format(info.st_size))
#启动路径下的文件
os.startfile('calc.exe')
#启动python解释器
os.startfile('C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Python 3.14\Python 3.14 (64-bit).lnk')



3.os.path子模块中常用的函数
获取目录或文件的绝对路径os.path.abspath()
判断目录或文件在磁盘中是否存在os.path.exists()
拼接路径os.path.join()
分隔文件的名和文件的后缀名os.path.spilttext()
提取文件名os.path.basename()
提取路径os.path.dirname()
判断一个路径是否是有效路径os.path.isdir()
判断一个路径是否是有效问件os.path.isfile()
import os.path
print('获取目录或文件的绝对路径:',os.path.abspath('./aa.txt'))#./绝对路径
print('判断目录或文件在磁盘上是否存在:',os.path.exists('./aa.txt'))
print('判断目录或文件在磁盘上是否存在:',os.path.exists('./ad.txt'))
print('拼接路径',os.path.join('D:\python\Lib\site-packages\模块','./aa.txt'))
print('分割文件的名和文件后缀名:',os.path.splitext('b.txt'))#元组类型
print('提取文件名:',os.path.basename(r'D:\python\Lib\site-packages\文件\aa.txt'))#r去掉转义字符
print('提取路径:',os.path.dirname(r'D:\python\Lib\site-packages\文件\aa.txt'))
print('判断一个路径是否是有效路径:',os.path.isdir(r'D:\python\Lib\site-packages'))
print('判断一个路径是否是有效文件:',os.path.isfile(r'D:\python\Lib\site-packages\文件\aa.txt'))

二.网络编程
1.对网络编程中的概念认识
(1)网络结构: 客户端服务器结构网络,是一种主从结构网络。服务器一般处于等待状 态,如果有客户端请求,服务器相应请求,建立连接提供服务。对等结构网络,也被称为点对点网络,每个节点之间是对等的。
(2)TCP(传输控制协议):一种高层次的协议,面向连接的可靠数据传输协议,如果有些数 据包没有收到会重发,并对数据包内容的准确性进行检查并保证数 据包顺序。
(3)IP:是一种低级的路由协议,他将数据拆分在许多小的数据包中,并通过网络将它们发送 到某一特定地址,但无法保证所有包都抵达目的地,也不能保证包的顺序。
(4)IP地址:每台计算机与众不同的标识。分为IPv4和IPv6,在命令行下使用ipconfig查看本 机的IP地址。
127.0.0.1称为回送地址,指本机。主要用于网络软件测试以及本地机进程间的通信,使用回送地址发送数据,不进行任何网络传输,只在本机进程间通信
(5)端口:设备与外界通讯交流的出口,通过端口号确保数据准确送达目标。范围是0~65535。
80-HTTP,21-FTP
(6)UDP协议:又被称为用户数据包协议,面向无连接的协议,只要知道对方的IP地址和端 口,就可以直接发送数据包,由于是面向无连接的,所以无法保证数据一 定会到达接收方。
(7)Socket:网络上的两个程序,通过一个双向链接,实现数据的交换。这个双向链路的一端称为一个socket。通常用来实现客户端和服务器的连接。一个socket有一个IP地址和一个端口号确定,从输入端端口号到输出端端口号一致。
(8)TCP/IP协议中的四个层次

(9)TCP/IP协议数据发送和数据接收

(10)TCP协议的三次握手

2.TCP服务器端代码和客户端代码编写
客户端代码编写 服务器端代码编写
(1)创建socket对象 (1)创建socket对象
(2)标明IP地址和主机端口 (2)绑定IP地址和端口
(3)发送数据 (3)使用listen进行监听
(4)关闭服务器 (4)等待客户端的连接
(5)接收来自客户端的数据
(6)关闭socket
import socket
#(1)创建socket对象
client_socket=socket.socket()#模块名.类名(),客户端不需要加参数
#(2)标明IP地址和主机端口
ip='127.0.0.1'#服务器的IP和端口
port=8888
client_socket.connect((ip,port))
print('与服务器的连接建立成功')
#(3)发送数据
client_socket.send('weclome to python world'.encode('utf-8'))#发送编码encode
#(4)关闭服务器
client_socket.close()
print('发送完毕')
#先运行服务器后运行客户端
from socket import socket,AF_INET,SOCK_STREAM #从模块中导入类
#AF_INET用于Internet之间的进程通信 SOCK_STREAM表示 TCP协议编程
#(1)创建socket对象
server_socket=socket(AF_INET,SOCK_STREAM)
#(2)绑定IP地址端口
ip='127.0.0.1'
port=8888
server_socket.bind((ip,port))
#(3)使用listen进行监听
server_socket.listen(5)
print('服务器已经启动')
#(4)等待客户端的连接
client_socket,client_addr=server_socket.accept()#accept接收到的内容,系列解包赋值,分别赋给两个变量
#(5)接收来自客户端的数据
data=client_socket.recv(1024)#接受1024个,从client_socket内容中接收数据并给data
print('客户端发送过来的数据为:',data.decode('utf-8'))#要求客户端发送过来的数据是使用utf-8进行编码的,接收解码decode
#(6)关闭socket
server_socket.close()

3.TCP多次通信服务器和客户端代码编写
在实现多次通信的过程中要用到while循环,以bye作为循环是否进行的循环条件,对接收到的数据进行判断,由客户端发送过来接受并由服务器端向客户端发送client_socket的数据,client_socket的数据为客户端和服务器之间传输的专用存储数据的名字。
#from...import...不需要加模块名
#import...需要加模块名
import socket
#(1)创建socket对象
socket_obj=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
#(2)绑定IP和端口号
socket_obj.bind(('127.0.0.1',8888))
#(3)开始监听
socket_obj.listen(5)#最多同时等待5个客户端连接
#(4)等待服务器连接
client_socket,client_addr=socket_obj.accept()#client_addr:客户端ip+端口.client_socket:客户端与服务器建立的通信通道
#(5)接收数据
info=client_socket.recv(1024).decode('utf-8')
#(6)准备执行多次通信
while info!='bye':#初始化变量info,循环条件
if info!='':
print('接收到的数据是',info)
data=input('请输入要发送的数据:')
client_socket.send(data.encode('utf-8'))#由客户端发送过来接受并由服务器端向客户端发送client_socket的数据
if data=='bye':#对发送的数据再次做判断
break
info=client_socket.recv(1024).decode('utf-8')#改变变量,对服务器端接收到的数据并再次做判断
#(7)关闭对象
socket_obj.close()
import socket
client_socket=socket.socket()
client_socket.connect(('127.0.0.1',8888))#一旦服务器与客户端建立连接,所有收发数据必须用client_socket完成
print('已经与服务器成功建立联系')
info=''
while info!='bye':
send_data=input('请客户端输入要发送的数据:')
client_socket.send(send_data.encode('uft-8'))
if send_data=='bye':
break
info=client_socket.recv(1024).decode('uft-8')
print('收到服务器的响应数据:',info)
client_socket.close()

4.UDP的一次双向通信
与TCP的整体思路相同,但要用sendto和recvfrom来传输和接收数据,发送和接收数据时要记得指定接收方的IP地址和端口
from socket import socket,AF_INET,SOCK_DGRAM
#(1)创建socket对象
send_socket=socket(AF_INET,SOCK_DGRAM)
#(2)准备发送数据
data=input('请输入要发送的内容:')
#(3)指定接收方的IP地址和端口
ip_port=('127.0.0.1',8888)
#(4)发送数据
send_socket.sendto(data.encode('utf-8'),ip_port)#sendto(data,(ip,port))
#接受来自接收方的回复数据
recv_data,addr=send_socket.recvfrom(1024)#需要以元组(data,adress)类型进行接收
print('接收到的数据为:',recv_data.decode('utf-8'))
#(5)关闭socket对象
send_socket.close()
from socket import socket,AF_INET,SOCK_DGRAM
#(1)创建socket对象
recv_socket=socket(AF_INET,SOCK_DGRAM)
#(2)绑定IP地址和端口
recv_socket.bind(('127.0.0.1',8888))
#(3)接受来自对方发送的数据
recv_data,addr=recv_socket.recvfrom(1024)
print('接收到的数据为:',recv_data.decode('utf-8'))
#(4)准备回复对方的数据
data=input("请输入要回复的数据:")
#(5)回复
recv_socket.sendto(data.encode('utf-8'),addr)
#(6)关闭
recv_socket.close()

5.模拟客服咨询的小程序
使用while循环来进行多次通信,服务人员要先接收数据判断接收到的数据并决定是否终止通信,若为中断则要准备回复对方的数据,接收的数据和发送的数据协议要相同且编码形式也要一样。
from socket import socket,AF_INET,SOCK_DGRAM
#编写socket对象
recv_socket=socket(AF_INET,SOCK_DGRAM)
#绑定IP地址和端口
recv_socket.bind(('127.0.0.1',8888))
while True:#必须用break终止语句
#接受发送过来的数据
recv_data,addr=recv_socket.recvfrom(1024)
print('客户说:',recv_data.decode('utf-8'))
if recv_data.decode('utf-8')=='bye':
break
#准备回复对方的数据
data=input('客服回:')
recv_socket.sendto(data.encode('utf-8'),addr)
recv_socket.close()
from socket import socket,AF_INET,SOCK_DGRAM
send_socket=socket(AF_INET,SOCK_DGRAM)
ip_port=('127.0.0.1',8888)
while True:
data=input('客户说:')
send_socket.sendto(data.encode('utf-8'),ip_port)
if data=='bye':
break
recv_data,addr=send_socket.recvfrom(1024)
print('客服回:',recv_data.decode('utf-8'))
send_socket.close()

三.进程与线程
1.基础概念认识
(1)进程:是指启动后的程序,系统会为进程分配内存空间。在进程中,每个进程的内部数据和状态都是完全独立的。一个进程就是一个执行中的程序,而每个进程都有自己独立的一块内存空间和一组系统资源
(2)程序:是指一系列有序指令的集合,使用编程语言所编写,用于实现一定的功能。
(3)线程:是CPU可调度的最小单位,被包含在进程中,是进程中实际的运作单位。一个进程中可以拥有N多个线程并行执行,而每个线程并行执行不同的任务
(4)进程池:创建一个进程池,并设置进程池中最大的进程数量。一次可执行多次任务。
(5)并发:指两个或多个事件统一时间间隔发生,多个任务被交替轮换这执行。吃完苹果拿快递,拿完快递吃苹果。
(6)并行:指两个或多个事件在同一时刻发生,多个任务在同一时刻在多个处理器上同时执行。三件事同时干。
(7)队列:先进先出的数据结构,进程之间可以通过队列进行通信。
2.创建进程和线程以及进程池的方式
创建进程的方式:
(1)process(group=None,target,name,args,kwargs)
Group:表示分组,实际上不使用,默认为None
Target:表示子进程要执行的任务,支持函数名
Name:表示子进程的名称
Args:表示调用函数的位置参数,以元组的形式传递
Kwards:表示调用函数的关键字参数,以字典的形式进行传递
(2)class 子进程(Process):
Pass
(3)创建进程池:进程池对象=Pool(N)
(4)创建队列:队列对象=Queue(N)
(5)创建线程t=Thread(group,target,name,args,kwargs)
group:创建线程对象的进程组
target:创建的线程对象所要执行的目标函数
name:创建线程对象的名称,默认为“Tread-n”
args:用元组以位置参数的形式传入target对应函数的参数
kwargs:用字典以关键字参数的形式传入target对应函数的参数
(6)自定义模块下的类
实现run方法
3.函数式创建子进程
定义函数,该函数为子进程要执行的内容,紧接着创建if __name__=='__main__':,主进程开始进行,用空列表来储存子进程,在遍历循环中,p=Process(target=test)创建子进程,并在循环中启动子进程,将子进程添加到列表中,其中遍历列表中的五个子进程,遍历阻塞主进程,每个子进程都要监听到阻塞行为才能确保主进程成功阻塞。正常情况下主进程先执行完子进程后执行完,改变执行先后顺序,需要用join()。
from multiprocessing import Process
import os,time
#函数中的代码是进程要执行的代码
def test():
print(f'我是子进程,我的PIP是{os.getpid()},我的父进程是{os.getppid()}')
time.sleep(1)
if __name__=='__main__':
print('主进程开始执行')
lst=[]
#创建五个子进程
for i in range(5):
#创建子进程
p=Process(target=test)
#启动子进程
p.start()
#将启动中的程序添加到列表中
lst.append(p)
#遍历lst,列表中的五个子进程
for item in lst:#item类型为Process类
item.join()#遍历阻塞主进程,每个子进程都要监听到阻塞行为才能确保主进程成功阻塞
print('主进程执行结束')
#正常情况下主进程先执行完子进程后执行完,改变执行先后顺序,需要用join()

4.Process类中常用的属性和方法
|
name |
当前进程实例别名,默认为Process-N |
|
pid |
当前进程对象的PID值 |
|
is_alive() |
进程是否执行完,没执行完结果为true,否则为false |
|
join(timeout) |
等待结束或等待timeout秒 |
|
start() |
启动进程 |
|
run() |
如果没有指定target参数,则启动进程后,会调用父类中的run方法 |
|
terminate() |
强制终止进程 |
from multiprocessing import Process
import os,time
#函数式方式创建子进程
def sub_process(name):
print(f'子进程PID:{os.getpid()},父进程{os.getppid()}---------{name}')
time.sleep(3)
def sub_process2(name):
print(f'子进程PID:{os.getpid()},父进程{os.getppid()}---------{name}')
time.sleep(3)
if __name__=='__main__':
#主进程
print('主进程开始执行')
for i in range(3):
#创建子进程
p1=Process(target=sub_process,args=('lcx',))#若不传参,则会调用执行run不执行子进程
p2=Process(target=sub_process2,args=(19,))#如果只有一个参数元组中也要有逗号
#启动子进程
p1.start()
p2.start()
print(p1.name,'是否执行完毕',p1.is_alive())
print(p2.name,'是否执行完毕',p2.is_alive())
print(p1.name,'PID是:',p1.pid)
print(p2.name,'PID是:',p2.pid)
p1.join()
p2.join()
print(p1.name, '是否执行完毕', p1.is_alive())
print(p2.name, '是否执行完毕', p2.is_alive())#执行过后不再活跃
#终止进程
#p1.terminate()
print('父进程执行完毕')

5.继承式创建子进程
与类中子类和父类的继承相似,通过继承父类Process来调用函数,创建子进程。
from multiprocessing import Process
import os,time
#自定义一个类
class SubProcess(Process):
#编写一个初始化方法
def __init__(self,name):
#调用父类中的初始化方法
super().__init__()
self.name=name
#重写父类中的run方法
def run(self):
print(f'子进程的名称{self.name},PID:{os.getpid()},父进程的PID:{os.getppid()}')
if __name__=='__main__':
print('父进程开始执行')
lst=[]
for i in range(5):
p1=SubProcess(f'进程:{i}')
#启动进程
p1.start()
lst.append(p1)
#阻塞一下主进程
for item in lst:
item.join()
print('父进程执行结束')

6.进程池的使用
|
apply_async(func,args,kwargs) |
使用非阻塞方式调用函数func |
|
apply(func,args,kwargs) |
使用阻塞方式调用函数func |
|
close() |
关闭进程池,不再接收新任务 |
|
terminate() |
不管任务是否完成,立即终止 |
|
join() |
阻塞主进程,必须在terminate()或close()后使用 |
from multiprocessing import Pool
import time,os
def task(name):
print(f'子进程的pid{os.getpid()},执行的任务{name}')
time.sleep(2)
if __name__=='__main__':
start=time.time()
print('父进程开始执行')
#创建进程池
p=Pool(3)#同时执行三个任务
#创建任务
for i in range(10):
#以非阻塞的方式
p.apply_async(func=task,args=(i,))#apply阻塞子进程
p.close()#关闭进程池不再接收新任务
p.join() # 阻塞父进程,等待所有的子进程执行完毕后,才会执行父进程中的代码。若无该条件则会直接退出进程池结束父进程不执行子进程
print('所有子进程执行完毕,父进程执行结束')
print(time.time()-start)

7.进程之间的数据不共享
通过设置两个子进程和一个全局变量,整体执行完毕后,全局变量的值不受影响,且子进程1执行后对子进程2的执行无影响
from multiprocessing import Process
a=100
def add():
print('子进程1开始执行')
global a#全局变量声明
a+=30
print('a=',a)
print('子进程1执行完毕')
def sub():
print('子进程2开始执行')
global a # 全局变量声明
a-=30
print('a=',a)
print('子进程2执行完毕')
if __name__ == '__main__':
print("父进程开始执行")
print('a=', a)
p1=Process(target=add)
p2=Process(target=sub)
p1.start()
p2.start()
p1.join()
p2.join()
print('父进程执行结束')
print('a=',a)#执行结束后没变

8.队列的基本使用
队列为一进一出,第一个进第一个出,也可以用来判断队列中的信息
|
qsize() |
获取当前队列包含的消息数量 |
|
empty() |
判断队列是否为空,为空结果为True,否则为False |
|
full() |
判断队列是否满了,满结果为True,否则为False |
|
get(block=True) |
获取队列中的一条消息,然后从队列中移除,block的默认值为True |
|
get_nowait() |
相当于get(block=False),消息队列为空时,抛出异常 |
|
put(item,block=True) |
将item消息放入队列,block的默认值为True |
|
put_nowwait(item) |
相当于put(item,block=False) |
from multiprocessing import Queue
if __name__ == '__main__':
#创建一个队列
q = Queue(3)#调用类名创建对象,最多可以接收3条信息
print('队列是否为空',q.empty())
print('队列是否为满', q.full())
#向队列中添加信息
q.put('hello')
q.put('world')
print('队列是否为空', q.empty())
print('队列是否为满', q.full())
q.put('python')
print('队列是否为空', q.empty())
print('队列是否为满', q.full())
print('队列当中信息个数', q.qsize())
#出队
print( q.get())#将第一个入对的拿出
print('队列当中信息个数', q.qsize())
#入队
q.put_nowait('pycharm')#不等待入队
#q.put('world')#一直等待入队
#遍历队内
if not q.empty():
for i in range(q.qsize()):
print(q.get_nowait())#不等待出队
print('队列是否为空', q.empty())
print('队列是否为满', q.full())
print('队列当中信息个数', q.qsize())

9.使用队列实现进程之间的通信
首先将数据传入队列中,并编写函数出队,通过观看运行结果可知先入队的先出队。
from multiprocessing import Queue, Process
import time
a=100
def write_msq(q):#q队列并将其作为参数传入
global a
if not q.full():
for i in range (6):
a-=10
q.put(a)
print('此时a的值为:',a)
#出队 写和出用的同一个队列
def read_msq(q):
time.sleep(2)
while not q.empty():
print('出队时a的值:',q.get())
if __name__ == '__main__':
print('父进程开始执行')
q=Queue()#由父进程创建队列,没有指定参数,说明可接受消息的个数没有上线
#创建两个子进程
p1=Process(target=write_msq, args=(q,))
p2=Process(target=read_msq, args=(q,))
#启动两个子进程
p1.start()
p2.start()
#等待写的进程执行完毕,再去执行主进程
print('父进程执行完毕')

10.函数式创建线程
编写函数,创建子进程,启动子进程。主线程负责执行 main中的代码,Thread-1线程执行三次循环,Thread-2线程执行三次循环。三个线程又是并发执行,三个线程并行执行test函数。因此谁先执行取决于CPU的调度。
import threading#模块
from threading import Thread#从模块中导入类
import time
#编写函数
def test():
for i in range(3):
time.sleep(1)
print(f'线程{threading.current_thread().name}正在实行{i}')
if __name__ == '__main__':
start=time.time()
print('主线程开始执行')
#创建子线程
lst=[Thread(target=test) for i in range(2)]
for item in lst:
#启动子线程
item.start()
for item in lst:
item.join()
print(f'一共耗时{time.time()-start}')
#主线程负责执行 main中的代码,Thread-1线程执行三次循环,Thread-2线程执行三次循环
#三个线程又是并发执行,三个线程并行执行test函数

11.继承式创建线程
与继承式创建子进程的流程相近
import threading
from threading import Thread
import time
class SubThread(Thread):
def run(self):
for i in range(3):
time.sleep(1)
print(f'线程{threading.current_thread().name}正在实行{i}')
if __name__ == '__main__':
print('主线程开始执行')
lst=[SubThread() for i in range(2)]
for item in lst:
item.start()
for item in lst:
item.join()
print('主线程执行完毕')

12.线程之间的数据共享
通过创建两个子线程进行运行,发现全局变量在子线程1的执行下影响到子线程2的执行结果了,最终主程序执行完毕后,a的值也发生了改变。
from threading import Thread
a=100
def add():
print('加线程开始执行')
global a
a+=10
print(f'a的值为{a}')
print('加线程执行完毕')
def sub():
print('减线程开始执行')
global a
a-=50
print(f'a的值为{a}')
print('减线程执行完毕')
if __name__=='__main__':
print('主线程开始执行')
print(f'a的值为{a}')
add=Thread(target=add,args=())
sub=Thread(target=sub,args=())
add.start()
sub.start()
add.join()
sub.join()
print('主线程执行结束')
print(f'a的值为{a}')

13.多个线程共享数据带来的问题
由于线程在执行时是并行,在调用函数时并发,因此会出现刚子线程1调用,子线程2也正在调用,词用要用lock模块对其进行上锁,防止其他子线程对其调用。
import threading#模块
from threading import Thread,Lock#从模块中导入类
import time
ticket=50#一共有50张票
lock_obj=Lock()#创建锁对象
def sale_ticket():#线程所要执行的
global ticket
#每个排队窗口假设有100人
for i in range (100):#每个窗口要执行100次循环
lock_obj.acquire()#上错,不能锁循环,否则会导致一个进程执行完循环才能让其他进程执行
if ticket>0:
print(f'{threading.current_thread().name}正在出售第{ticket}张票')
ticket-=1
time.sleep(1)
lock_obj.release()#解锁,释放锁后哪个线程执行是随机的
if __name__ == '__main__':
for i in range(3):#创建三个进程,代表三个窗口
t=Thread(target=sale_ticket)
t.start()#启动线程

14.生产者与消费者问题
生产数据并存放到数据库,消费者要从数据库中取出数据
from threading import Thread
from queue import Queue
import time
#创建一个生产者类
class Producer(Thread):
def __init__(self,name,queue):
Thread.__init__(self,name=name)
self.queue = queue
def run(self):
for i in range(1,6):
print(f'{self.name}将产品{i}放入队列')
self.queue.put(i)
time.sleep(1)
print('生产者完成了所有数据的存储')
#创建一个消费者类
class Consumer(Thread):
def __init__(self,name,queue):
Thread.__init__(self,name=name)
self.queue = queue
def run(self):
for _ in range(5):
value=self.queue.get()
print(f'消费者线程:{self.name}取出了{value}')
print('消费者线程完成了所有数据的取出')
if __name__ == '__main__':
#创建队列
queue = Queue()
#创建生产者线程
p=Producer('Producer',queue)
#创建消费者线程
c=Consumer('Consumer',queue)
#启动线程
p.start()
c.start()
p.join()
c.join()

四.实战练习
1.车牌归属地
温习正则表达式,for循环,format格式化的使用,索引的使用
import re
lst=[]
for i in range(3):
plate_number=input('请输入您的车牌号:')
pattern = '\D\D\d*'
if re.findall(pattern,plate_number):
lst.append(plate_number)
for num in lst:
area=num[0:1]
print(f'{num}归属地为{area}')

2.提取字符串中所有数字并求和
import re
x=input('请输入一个字符串:')
print(x)
num=re.findall('\d',x)
print('提取到的数字列表为:',num)
y=0
for i in num:
y+=int(i)
print('累加和为:',y)
print('-------------------------------------------')
def get_isdigit(s):
a=0
lst=[]
for item in s:
if item.isdigit():
lst.append(int(item))
a=sum(lst)
return a,lst
a=input('请输入一个字符串:')
s,lst=get_isdigit(x)#解包
print('提取的数字列表为:',lst)
print('累加和为:',s)

更多推荐



所有评论(0)