【笔记】Python基础学习(五)

这篇博客介绍了Python中日期时间的表示方式,包括时间戳、时间元组和格式化时间,详细讲解了Calendar、time和datetime模块。接着讨论了文件处理,涉及OS模块、open()函数、configparser、csv模块以及xlrd和xlwt模块。最后,文章深入探讨了进程、线程和协程的概念,解析了全局解释器锁、多线程实现,如_thread和threading模块,以及进程并行和协程应用,包括concurrent.futures模块。

第12章:日期与时间

1、日期与时间的类型

  • 在Python中,日期与时间通常有3种表示方式,即:时间戳,时间元组和格式化时间
  • time模块是用来生成日期时间
  • 时间戳是指格林尼治1970年1月1日00时00分00秒
  • 时间元组包含时间的年、月、日、时、分、秒、一年中第几周、一年中第几天、是否为夏令时,它可以使用time模块的函数strptime( )或localtime( )生成
  • 格式化时间是根据实际需要将时间按一定的格式表示,可以使用time模块的strftime( )函数来格式化时间
import time

# 示例1
t = time.time()
print('当前时间的时间戳为:', t)

# 示例2
struct_time = time.localtime(t)
print('返回的元组:', struct_time)

# 示例3
t1 = time.localtime(t)
print('获取完整年份:', time.strftime('%Y', t1))
print('获取月份:', time.strftime('%m', t1))
  • 常用时间单位的格式化符号
格式符号
%y两位数的年份表示,(00-99)
%Y四位数的年份表示,(000-9999)
%m月份(01-12)
%d月中的某一天(0-31)
%H24小时制的小时数(0-23)
%I12小时制的小时数(01-12)
%M分钟数(0-23)
%S秒数(00-59)

2、Calendar模块

  • Calendar是Python中与日历相关的模块,它定义了Calendar、TextCalendar和HTMLCalendar类,并且对外提供了很多函数
  • Calendar是TextCalendar和HTMLCalendar的基类
""""
Calendar模块的函数方法
"""
import calendar

# 将星期日设置为一周的第一天
calendar.setfirstweekday(firstweekday=6)

# 返回一周的第一天
print(calendar.firstweekday())

# 闰年判断
print("2021是否为闰年:", calendar.isleap(2021))
print("2008是否为闰年:", calendar.isleap(2008))

"""
Calendar实例方法
"""
c = calendar.Calendar()
# 获取一周每天的数字
print(list(c.iterweekdays()))

"""
TextCalendar实例方法
"""
tc = calendar.TextCalendar()
# 返回8月份的日历
print(tc.formatmonth(2022, 8))

"""
HTMLCalendar实例方法
"""
hc = calendar.HTMLCalendar(firstweekday=6)
# 返回8月份的日历
print(hc.formatmonth(2022, 8, withyear=False))

3、time模块

  • time模块是Python中用于提供存取与转换时间的函数(使用UTC-格林尼治时间表示)

  • time模块定义了很多函数及方法,这些函数及方法可以分为系统级别和线程级别

  • 系统级别是获取当前操作系统的时间;线程级别是获取线程的执行时间等

  • time模块常用的函数及方法:

import time

# localtime()是把以秒为单位的时间转换为本地时间,返回的是一个元组
# 可选参数secs代表需要转化的时间,无参数时表示当前的时间
print("本地时间为:", time.localtime())

# gmtime()是把以秒为单位的时间转换为UTC时间(格林尼治时间)
# 可选参数secs代表需要转化的时间戳,无参数时表示当前的时间
print("当前UTC时间为:", time.gmtime())

# mktime()将localtime或gmtime的返回值转换为以秒为单位的浮点数
# 参数必须是结构化时间或9位时间元素的元组
tl = time.localtime()
print("tl转换为:", time.mktime(tl))

# ctime()将一个时间戳转换为字符串格式的时间格式
# 可选参数secs代表需要转化的时间戳,无参数时表示当前的时间
print("当前时间", time.ctime())

# sleep()将目前的程序转入等待状态,等待时间按秒
# 等待3秒
time.sleep(3)

# strptime()根据指定的时间格式把一个时间字符串转为元组格式
print(time.strptime('2022-04-17', '%Y-%m-%d'))

4、datetime模块

  • datetime是Python中与日期和时间相关的模块,包括date和time的所有信息
  • datetime模块中一共定义了5个类,分别为date类、time类、datetime类、timedelta类、tzinfo类
  • datetime中每个类的说明:
    • date:表示日期的类,主要属性有year、month、day
    • time:表示时间的类,主要属性有hour,minute、second、microsecond和tzinfo
    • datetime:表示日期和时间的组合类,主要属性有year、month、day、hour,minute、second、microsecond和tzinfo
    • timedelta:表示时间间隔类,用于时间的计算
    • tzinfo:表示时区信息类
import datetime

# date类示例
d = datetime.date.today()
print(d)
print('年:', d.year)
print('月:', d.month)
print('日:', d.day)

# time类示例
t = datetime.time(20, 40, 20, 100666)
print(t)
print('时', t.hour)
print('分', t.minute)
print('秒', t.second)
print('毫秒', t.microsecond)

# datetime类示例
dd = datetime.datetime.now()
print(dd)
print('年:', dd.year)
print('月:', dd.month)
print('日:', dd.day)
print('时', dd.hour)
print('分', dd.minute)
print('秒', dd.second)
print('毫秒', dd.microsecond)

# timedelta类示例
dt = datetime.datetime.now()
dw = datetime.timedelta(weeks=1)
print('当前日期时间加上1周后的日期时间', dt + dw)
print('当前日期时间减去1周后的日期时间', dt - dw)

第13章:文件处理

1、OS模块

  • Python的OS模块是用来处理文件和目录,它提供了查找目录中所有文件的方法
  • OS模块中常用的函数及方法说明:
import os

# os.getcwd():查看当前运行环境所在的路径信息
print(os.getcwd())

# os.walk():获取目录下所有的目录和文件夹信息
for root, dirs, files in os.walk(r"D:\其他文档"):
    for name in files:
        print(os.path.join(root, name))
    for name in dirs:
        print(os.path.join(root, name))
        
# os.curdir:返回当前目录的名称
print(os.curdir)

# os.pardir:返回当前目录的父目录的名称
print(os.pardir)

# os.makedirs():生成一个多层递归目录
os.makedirs(r'D:\其他文档')

# os.removedirs():目录为空,删除并返回上级目录,如果上级目录也为空,就执行同样的操作,以此类推
os.removedirs(r'D:\其他文档')

# os.mkdir():创建一个目录
os.mkdir(r'D:\新目录')

# os.rmdir():删除某个目录,若目录不为空,则无法删除并提示异常
os.rmdir(r'D:\其他文档')

# os.remove():删除文件,不能删除文件夹
os.remove(r'D:\其他文档\test.txt')

# os.stat():获取文件/目录信息,并获取到文件的大小
info = os.stat(r'D:\其他文档\test.txt')
print(info)

# os.system():运行Shell命令
print(os.system('ipconfig'))

# os.environ:获取操作系统的环境变量
print(os.environ)

2、内置函数open()

  • Python使用open( )函数实现txt文件的读写
  • 具体读写方法如下:
f = open('test.txt','w',encoding='utf-8')
# 文件写入
f.write('This is Python')
f.close()
  • open( )函数共有7个参数,每个参数说明如下:
open(file, mode='r', buffering=None, encoding=None, errors=None, newline=None, closefd=True)

# 参数说明:
file:必选,代表文件路径
mode:可选,代表文件打开模式(r w 和 a)
buffering:设置缓冲
encoding:设置编码格式
errors:错误信息
newline:区分换行符
closefd:读写完后是否关闭文件
  • 当完成文件读写后,必选调用close( )函数关闭已打开的文件
  • 使用with…as …语句进行文件的读写,具体使用方法如下:
with open('test.txt','w') as f:
    print('This is Python')

3、内置模块configparser

  • Python的内置模块configparser支持读写CONF和INI类型的文件

  • INI类型配置文件示例:

# test.ini文件
[settings]			## section
username = admin	 ## options
password = 123456	 ## options(键值对)
  • 读取配置文件:
import configparser

# 创建一个configparser对象
cf = configparser.ConfigParser()

# 读取配置文件,生成文件对象
filename = cf.read('test.ini')

# sections()得到所有section,以列表返回
sec = cf.sections()

# options(section)获取section下的所有option
opt = cf.options("settings")

# items()获取section下的所有键值对
value = cf.items("settings")

# get(section,option)得到section中option的值
username = cf.get("settings", "username")
password = cf.getint("settings", "password")
print(username, password)
  • 生成新的配置文件:
import configparser

# 创建一个configparser对象
cf = configparser.ConfigParser()

# 对配置文件写入内容
# add_section添加section
cf.add_section("mysql")
# set()写入对应的键值对
cf.set("mysql", "username", "admin")
cf.set("mysql", "password", "pwd123")

# 使用open()创建新的文件
with open("mytest.ini", "w+") as f:
    cf.write(f)
  • 修改配置文件:
import configparser

# 创建一个configparser对象
cf = configparser.ConfigParser()

# 读取配置文件
cf.read("mytest.ini")

# 修改配置
cf.set("mysql", "user", "admin1")
# 新增配置
cf.set("mysql", "IP", "127.0.0.1")
cf.set("mysql", "IP2", "127.0.0.2")

# 删除配置
cf.remove_option("mysql", "IP2")

# 使用open()将修改写入文件
with open("mytest.ini", "w+") as f:
    cf.write(f)
  • configparser模块主要函数及方法说明:
read(filename):读取配置文件,生成文件对象
sections():获取所有section并以列表的方式返回
options(section):获取section下所有的option
items():获取section的所有键值对
get(section,option):获取section中option值,返回string
getint(section,option):获取section中option值,返回int
add_section():添加section
set(section,option,value):给section添加键值对
remove_optiin():删除option
remove_section():删除section,section下如果有值,全部一并删除

4、内置模块csv

  • Python中自带csv模块来处理CSV文件

  • 在CSV文件中写入数据示例:

import csv

# 打开csv文件
# newline最好设置成空,否则每次写入一行数据,数据间会出现一行空白行
csvfile = open("csv_test.csv", "w", newline="")
# 将文件加载到csv对象中
writer = csv.writer(csvfile)
# 写入首行数据
writer.writerow(["姓名", "年龄", "电话"])
# 写入多行数据
data = [('P', '18', '20220420001'),
        ('Y', '22', '20220420002'),
        ('Z', '34', '20220420003')]
writer.writerows(data)
# 关闭csv对象
csvfile.close()
  • 在CSV文件中读取数据有两个函数,分别是reader和DictReader,两者都是接收一个可迭代的对象,返回一个生成器。
  • reader函数是将一行数据以列表形式返回,DictReader函数返回的是一个字典,值是单位格的值,键是表头
import csv

csvfile = open("csv_test.csv","r")

# 以列表的形式输出
reader = csv.reader(csvfile)

# 以字典形式输出,第一行作为字典的键
# dreader = csv.DictReader(csvfile)

rows = [row for row in reader]
print(rows)

5、xlrd、xlwt模块

  • Python中xlrd、xlwt、pyExcelarator和openpyxl都可以操作Excel文件

  • pyExcelarator仅支持2003版,openpyxl仅支持2007版,xlrd支持任何版本读取,xlwt支持任何版本写入

  • xlrd、xlwt安装:

pip install xlrd
pip install xlwt
  • xlwt实现Excel写入:
import xlwt

# 新建Excel文件
wb = xlwt.Workbook()
# 新建sheet页
ws = wb.add_sheet("Python", cell_overwrite_ok=True)
# 定义字体对齐方式对象
alignment = xlwt.Alignment()
# 设置字体对齐方式
alignment.horz = xlwt.Alignment.HORZ_CENTER
alignment.vert = xlwt.Alignment.VERT_CENTER
# 定义格式对象
style = xlwt.XFStyle()
style.alignment = alignment
# 写入数据wb.write(行,列,内容)
ws.write_merge(0, 0, 0, 5, "xlwt写入Excel示例", style)
for i in range(2, 7):
    for k in range(5):
        ws.write(i, k, i + k)
    ws.write(i, 5, xlwt.Formula('SUM(A' + str(i + 1) + ':E' + str(i + 1) + ')'))
# 保存文件
wb.save('file.xls')
  • xlrd实现Excel写入:
import xlrd
wb = xlrd.open_workbook('fileName.xls')

# 获取Sheets总数
ws_count = wb.bsheets

# 通过索引获取Sheets
ws = wb.sheets()[0]

# 通过sheets名获取Sheets
ws = wb.sheets_by_name("Excel")

# 获取整行的值
row_value = ws.row_values(3)

# 获取整列的值
row_col = ws.col_values(3)

# 获取所有行列
nrows = ws.nrows
ncols = ws.ncols

# 获取某个单元格内容cell
cell_F3 = ws.cell(2,5).value

# 使用行列索引获取某个单元格内容
row_F3 = ws.row(2)[5].value
col_F3 = ws.col(5)[2].value

第14章:进程、线程与协程

1、全局解释器锁

  • 全局解释器锁(GIL):GIL的设计理念是为了解决多线程之间的数据完整性和状态同步的问题,在任意时刻只有一个线程在解释器中运行,而当执行多线程的时候,由GIL来控制同一时刻只有一个线程能够运行

2、进程、线程与协程

  • 并行和并发

    • 并行是指不同的代码块同时执行,它是以多核CPU为基础的,每个CPU独立执行一个程序,各个CPU之间的数据相互独立,互不干扰
    • 并发是指不同的代码交替执行,它是以一个CPU为基础的,使用多线程等方式提高CPU的利用率,线程之间会相互切换,轮流被Python解释器执行
  • 进程、线程和协程

    • 进程都有自己的地址空间,简单来说它是一个执行中的程序
    • 线程是进程中的一个实体,被系统独立调度和分派的基本单位。线程自己不拥有系统资源,只拥有运行中必不可少的资源。同一进程中的多个线程并发执行,这些线程共享进程所拥有的资源。
    • 协程:这是一种比线程更加轻量级的存在,重要的是,协程不被操作系统内核管理,协程完全是由程序控制的,它的运行效率极高。协程的切换完全由程序控制,不像线程切换需要花费操作系统的开销,线程数量越多,协程的优势就越明显。协程不受GIL的限制,因为只有一个线程,不存在变量冲突。

3、实现多线程

  • Python的内置模块_thread和threading实现了多线程功能,日常开发中使用threading模块较多

3.1 _thread模块

  • _thread模块的使用语法:
_thread.start_new_thread(functioin,arg[,kwargs])
# start_new_thread() 拥有三个参数
# 必选参数function代表线程执行的函数
# 可选参数args被执行函数入参,以元组格式表示
# 可选参数kwargs被执行函数入参,以字典格式表示
  • 示例
import _thread
import time

# 被执行函数
def print_time(threadName,delay,**kwargs):
  print(kwargs,get("name"))
  for i in range(3):
    time.sleep(delay)
    print("%s:%s"%(threadName,time.ctime(time.time())))
    
# 创建两个线程
try:
  _thread.start_new_thread(print_time,("Thread-1",1),{"name":"Tome"})
  _thread.start_new_thread(print_time,("Thread-2",2))
except:
  print("Error:无法启动线程")

while 1:
  pass

3.2 threading模块

  • threading中定义的其他函数方法:
threading.currentThread():返回当前的线程变量。
threading.enumerate():返回一个包含正在运行的线程的列表,正在运行的线程是指线程启动后和结束前的状态。
threading.activeCount():返回正在运行的线程数量,与len(threading.enumerate())的结果等同。
  • threading定义了Thread类来处理线程,并定义了以下的实例方法:

    • run( ):用以表示线程活动的方法
    • start( ):启动线程
    • join([timeout]):等待线程执行结束,可选参数timeout用于设置超时时间
    • isAlive( ):返回线程是否正在执行
    • getName( ):返回线程名
    • setName( ):设置线程名
  • Threading拥有多种使用方式,比如普通使用方式、自定义线程类、守护进程、等待线程结束、多线程共享全局变量、互斥锁(递归锁)、信号量、线程事件等

  • 线程的普通使用方法

    • 是由threading.Thread( )方法实现,使用threading.Thread( )创建对象后,由对象调用start( )方法开始执行多线程
# 线程的普通使用方法
import time
import threading

def run(n,**kwargs):
  print("name",kwargs.get("name"))
  print("task",0)
  time.sleep(1)
  
if __name__ == '__main__':
  # target是要被执行的函数,args被执行函数的入参,元组形式;kwargs被执行函数的入参,字典形式
  t1 = threading.Thread(target = run, args = ("t1",), kwargs = {'name':'Tom'})
  t2 = threading.Thread(target = run, args = ("t2",), kwargs = {'name':'Lily'})
  t1.start()
  t2.start()
  print('执行完毕')
  • 自定义线程类
    • 是继承threading.Thread类来定义线程类的,其本质是重构Thread类的实例方法run(),将实例方法run()改为我们需要执行的代码,示例如下:
# 自定义线程类
import time
import threading

class MyThread(threading.Thread):
  def __init__(self,n):
    super(MyThread,self).__init__()
    self.n = n
    
  # 重构实例方法run()
  def run(self):
    print('task',self.n)
  	time.sleep(1)
    
if __name__ == '__main__':
  t1 = MyThread('t1')
  t2 = MyThread('t2')
  t1.start()
  t2.start()
  • 守护线程
    • 是当程序执行完毕后,无论线程是否执行完毕都会强制退出线程,首先使用threading.Thread()创建对象,再由该对象调用setDaemon( )方法,并传入参数True即可
# 守护线程
import time
import threading

def run(n):
  time.sleep(3)
  print("task",n)

  
if __name__ == '__main__':
  # target是要被执行的函数,args被执行函数的入参,元组形式;kwargs被执行函数的入参,字典形式
  t1 = threading.Thread(target = run, args = ("t1",))
  t1.setDaemon(True)
  t1.start()
  print('执行完毕')
  • 等待线程结束
    • 是程序使用线程执行部分功能的时候,需要线程的执行结果才能往下执行后面的功能,在线程的执行过程中,程序一直处于等待状态
# 等待线程结束
import time
import threading

def run(n):
  time.sleep(5)
  print("task",n)

if __name__ == '__main__':
    t = threading.Thread(target = run, args = ("t1",))
    t.strart
    # 等待线程执行结束
    t.join()
 	print('执行完毕')
  • 多线程共享全局变量
    • 是使用关键字global在线程执行的函数中定义全局变量
# 线程全局变量
import threading

# 初始化全局变量
g_num = 100

def work1():
    global g_num
    for i in range(3):
        g_num += 1
    print(f"in work1 g_num is:{g_num}")
    
def work2():
    global g_num
    for i in range(3):
        g_num += 1
    print(f"in work2 g_num is:{g_num}")

if __name__ == '__main__':
    t1 = threading.Thread(target = work1)
    t2 = threading.Thread(target = work2)
    t1.start()
    t2.start()
  • 互斥锁
    • 互斥锁是有lock类定义的,递归锁RLock类定义的,两者在使用上并无差异,但递归锁支持嵌套
    • 互斥锁(递归锁)解决多线程的同时读写同一条数据可能会出现脏数据
# 互斥锁
import time
import threading

def work():
    time.sleep(s)
    f = open("test.txt","a+")
    f.write(f"this is {t}\n")
    f.close()
    
if __name__ == '__main__':
    t1 = threading.Thread(target = work,args('t1',3))
    t2 = threading.Thread(target = work,args('t2',1))
    t1.start()
    t2.start()
  • 信号量
    • 是限制程序同时能执行多少条线程,它的使用与互斥锁(递归锁)大致相同,也是在线程执行的函数中调用相应的函数方法
# 信号量
import threading
import time

def run(n,semaphore):
    # 加锁
    semaphore.acquire()
    time.sleep()
    print('run the thread:%s\n'%n)
    semaphore.release()
    
if __name__ == '_main__':
    # 最多允许两个线程同时运行
    semaphore = threading.BoundedSemaphore(2)
    for i in range(4):
        t = threading.Thread(target=run,args=(f't-{i}',semaphore))
        t.start()
  • 线程事件
    • 是程序控制线程的执行,事件是一个简单的线程同步对象
    • 线程事件的处理机制是定义一个全局变量flag,如果flag是False,event_wait( )就处于阻塞状态,为True则event_wait( )不再阻塞
    • 主要方法:
      • clear( )将全局变量flag设置成False
      • set( )将flag设置为True
      • is_set( )判断全局变量是否为True
      • wait( )监听全局变量,当flag=False时,就一直处于阻塞状态
# 线程事件
import threading
import time

# 定义事件对象
event = threading.Event()

def t1():
    i = 0
    while1:
        if i < 3:
            # 将flag设置为False
            event.clear()
            print("阻塞中")
        elif i == 4# 将flag设置为True
            event.set()
            print("阻塞结束")
        time.sleep()
        # 判断flag是否为True,若是则终止循环
        if event.is_set():
        	break
        i += 1
        
def t2():
    while 1:
        print("This is T2")
        if event.is_set():
            break
        time.sleep(2)
        
t1 = threading.Thread(target = t2)
t2 = threading.Thread(target = t2)

t1.start()
t2.start()
# 监听事件
event.wait()

4、实现进程并行

  • 在Python中,多线程是无法利用CPU多核优势的,如果想要充分地使用CPU的多核资源,在Python中可以使用多进程
  • Python提供了内置模块multiprocessing,模块功能包括:创建进程、守护进程、等待进程结束、互斥锁、通信和共享数据、信号量、进程事件、进程池,并定义了Process、Queue、Pipe、Lock等组件。
  • 多进程与多线程在使用上大同小异,创建进程由Process类完成,其语法如下:
Process([group [,target [,name [,args [,kwargs]]]]])

语法的各个参数说明如下:
(1)参数group并无太大作用,参数值为None即可。
(2)参数target表示调用对象,即进程执行的函数。
(3)参数name为所创建的进程的名称。
(4)参数args表示进程执行的函数的参数,如args=(1, 2, 'anne',)。
(5)参数kwargs表示进程执行的函数的参数,如kwargs={'name': 'Tom', 'age': 18}。
# 使用Process类创建进程
import time
from multiprocessing import Process

def run(name):
    print(f'{name} is running')
    time.sleep(2)
    print(f'{name} is end')

if __name__ = '__main__':
    p1 = Process(target = run, args = ("anne",))
    p2 = Process(target = run, args = ("alice",))
    
    p1.start()
    p2.start()

# 继承和自定义Process类
class Run(Process)def __init__(self,name):
        super().__init__()
        self.name = name
        
    def run(self):
        print("%s is running" % self.name)
        time.sleep(2)
        print("%s is end" % self.name)
        
if __name__ == '__main__':
    p1 = Run("anne")
    p2 = Run("alice")
    p1.start()
    p2.start()
    print('主进程')
  • 守护进程
    • 是由Process类的实例化对象设置属性daemon实现的,当程序执行完成后,无论创建的进程是否执行完毕,它都会强制终止进程
# 守护进程
from multiprocessing import Process
import time

def run(name):
    print(f'{name} is running')
    time.sleep(2)
    print(f'{name} is end')
    
p1 = Process(target = run, args = ("Tom",))
# 设置守护线程
p1.deamon = True
p1.start()
print('主进程')
  • 等待进程结束
    • 是由Process类的实例化对象调用join()实现的,当所有进程执行完毕后,程序才会往下执行剩余的代码
# 等待进程结束
import time
from multiprocessing import Process

def run(name):
    print(f'{name} is running')
    time.sleep(2)
    print(f'{name} is end')

if __name__ = '__main__':
    p1 = Process(target = run, args = ("anne",))
    p2 = Process(target = run, args = ("alice",))
    
    p1.start()
    p2.start()
    p1.join()
    p2.join()
    print('主进程')
  • 互斥锁
    • 是限制多个进程在同一时间对数据进行读写操作,它使多个进程按照启动顺序依次执行
# 互斥锁
import time
from multiprocessing import Process,Lock

def work(t,lock):
    # 加锁
	lock.acquire()
    print("%s is running" % t)
    time.sleep(2)
    print("%s is end" % t)
    # 解锁
    lock.release()
    
if __name == '__main__':
    # 实例化锁
    lock = Lock()
    for i in range(3):
        p = Process(target = work, args = (i,lock))
        p.start()
  • 通信和共享数据
    • 使用Manager( )和Lock( )方法实现,Manager( )负责数据共享,Lock( )保护数据每次只能有一个进程操作
# 通信和共享数据
from multiprocessing import Manager,rocess,Lock

def work(d,lock):
    with lock:
        d['count'] -= 1
        
if __name__ == '__main__':
    lock = Lock()
    with Manager() as m:
        dic = m.dict({'count':100})
        p_1 = []
       	for i in range(100):
            p = Process(target = work, args = (dic,lock))
            p_1.append(p)
            p.start()
        for p in p_1:
            p.join()
        print(dic)
  • 信号量
    • 是限制程序同一时间能执行多少个进程,如果需要运行的进程多于限制数量,其他进程就会处于等待状态
# 信号量
from multiprocessing import Process,Semaphore
import time

def work(sem,user):
    sem.acquire()
    print("%s 占到一个位置:"% user)
    time.sleep(3)
    sem.release()
    
if __name__ == '__main__':
    sem = Semaphore(3)
    p_1 = []
	for i in range(9):
            p = Process(target = work, args = (sem,i,))
            p.start()
            p_1.append(p)
        for p in p_1:
            p.join()
  • 进程事件
    • 进程事件与线程事件是同一个概念,并且在使用上也是相同的
# 进程事件
from multiprocessing import Process,Event
import time

def p1(event):
    i = 0
    while 1:
        if i < 3:
            event.clear()
            print("阻塞中")
        elif i == 4:
        	event.set()
            print("阻塞结束")
            time.sleep(1)
        if event.is_set():
            break
         i += 1
        
def p2(event):
    while 1:
        print("This is P2")
    if event.is_set():
        break
    time.sleep(2)
    
if __name__ == '__main__':
    event = Event()
    pp1 Process(target = p1, args = (event,))
    pp2 Process(target = p2, args = (event,))
    pp1.start()
    pp2.start()
    event.wait()
  • 进程池
    • 由于多进程的执行效率是受CPU核数影响的,一个操作系统不可能无限开启多个进程,进程越多,执行效率反而会降低,因此我们可以通过进程池来控制进程数目
    • 进程池是由multiprocessing模块的Pool类创建的,其语法如下:
Pool([numprocess  [, initializer [, initargs]]])

参数说明如下:
(1)numprocess:创建的进程数,如果省略,就默认使用cpu_count()的值,即CUP核数。
(2)initializer:每个工作进程启动时要执行的可调用函数方法,默认为None。
(3)initargs:传递给initializer的参数。

当创建进程池后,由进程池对象调用实例方法实现不同的功能,实例方法的作用如下:
(1)apply(func [, args [, kwargs]]):以同步方式使用进程执行某个函数,进程会一直等待函数执行完成为止。
(2)apply_async(func [, args [, kwargs]]):以异步方式使用进程执行某个函数,必须与join()方法一起使用,否则程序结束了,进程也会强迫结束。
(3)close():关闭进程池。
(4)join():等待所有工作中的进程结束,此方法只能在close()或teminate()之后调用。
# 线程池实现同步多线程
from multiprocessing import Pool
import os,time

def work(n):
	print('%s run' % os.getpid())
	time.sleep(3)
	return n ** 2
	
if __name__ == '__main__':
	p = Pool(3)
	res_1 = []
	for i in range(5):
        # 同步
		res = p.apply(work,args=(i,))
		re_1.append(res)
	print(res_1)
# 线程池实现异步多线程
from multiprocessing import Pool
import os,time

def work(n):
	print('%s run' % os.getpid())
	time.sleep(3)
	return n ** 2
	
if __name__ == '__main__':
	p = Pool(3)
	res_1 = []
	for i in range(6):
        # 异步
		res = p.apply_sayns(work,args=(i,))
		re_1.append(res)
    p.close()
    p.join()
    print([res.get()for res in res_1])
  • apply_sayns( )可以设置回调函数
from multiprocessing import Pool

def work(n):
    return n ** 2

def after_work(result):
    print("回调函数:",result)
	
if __name__ == '__main__':
	p = Pool(3)
	res_1 = []
	for i in range(6):
        # 异步
		res = p.apply_sayns(work,args=(i,),callback=after_work)
		re_1.append(res)
    p.close()
    p.join()

5、concurrent.futures实现并行任务

  • Python标准库为我们提供了threading和multiprocessing模块编写相应的多线程/多进程代码
  • 从Python 3.2开始,标准库提供了concurrent.futures模块,它提供了ThreadPoolExecutor和ProcessPoolExecutor两个类,实现对threading和multiprocessing更高级的抽象,对编写线程池/进程池提供了直接的支持
from concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutor
import time


# 线程的执行方法
def print_value(value):
    print("Thread__" + str(value))

# 创建两个线程,每个线程执行print_value方法
def myThread(value):
    Thread = ThreadPoolExecutor(max_workers=2)
    Thread.submit(print_value, time.time())
    Thread.submit(print_value, time.time())


# 创建两个进程,每个进程执行myThread方法
def myProcess():
    pool = ProcessPoolExecutor(max_workers=2)
    pool.submit(myThread, time.time())
    pool.submit(myThread, time.time())


if __name__ == '__main__':
    myProcess()
  • 获取任务的执行结果
    • 为了获得所有线程或进程的执行结果,可以使用模块定义的as_completed( )方法获取,示例代码如下:
import random
import time
from concurrent import futures


# 定义线程执行任务
def task(n):
    time.sleep(random.random())
    return n, n / 10


# 创建多线程
Thread = futures.ThreadPoolExecutor(max_workers=2)
print('程序开始')
task_list = [Thread.submit(task, i) for i in range(5, 0, -1)]
for f in futures.as_completed(task_list):
    print('result:{}'.format(f.result()))
  • 设置回调函数
    • concurrent.futures的回调函数是当任务在等待执行之前被取消或者任务执行完成后所触发的函数,回调函数必须设有参数fn,参数fn代表Future的实例对象,即任务提交执行后所生成的对象
from concurrent import futures

def task(n):
    # 设置任务的返回值
    return n / 10

def done(fn):
    # 参数fn代表Future的实例对象
    # 判断线程的任务是否被取消
    if fn.cancelled():
        print('执行结果 {}:取消'.format(fn.value))
    # 判断线程的任务是否执行完成
    elif fn.done():
        error = fn.exception()
        # 判断线程的任务在执行过程中是否出现异常
        if error:
            print('执行结果 {} : 错误返回 : {}'.format(fn.value, error))
        else:
            result = fn.result()
            print('执行结果 {} : 正常返回 : {}'.format(fn.value, result))

if __name__ == '__main__':
    Thread = futures.ThreadPoolExecutor(max_workers=2)
    print('程序开始...')
    # 任务执行后将生成Future的实例对象
    f = Thread.submit(task, 5)
    # 为Future的实例对象设置属性value的值
    f.value = 66
    # 调用add_done_callback(fn)方法
    f.add_done_callback(done)
    # 获取执行结果
    result = f.result()
  • 取消任务
    • 当线程处于阻塞状态的时候,可以将线程取消执行
from concurrent import futures
import time

def task(n):
    time.sleep(3)
    return n / 10

def done(fn):
    if fn.cancelled():
        print('执行结果 {}:取消了'.format(fn.arg))
    elif fn.done():
        error = fn.exception()
        if error:
            print('执行结果 {} : 错误返回 : {}'.format(fn.arg, error))
        else:
            result = fn.result()
            print('执行结果 {} : 正常返回 : {}'.format(fn.arg, result))

if __name__ == '__main__':
    ex = futures.ThreadPoolExecutor(max_workers=1)
    print('程序开始...')
    tasks = []

    # 执行任务
    for i in range(3):
        print('开始执行任务: {}'.format(i))
        f = ex.submit(task, i+1)
        f.arg = i+1
        f.add_done_callback(done)
        tasks.append((i+1, f))

    # 取消任务
    for i, task_obj in tasks:
        # 使用cancel()取消任务
        # 成功取消后会自动执行回调函数
        if not task_obj.cancel():
            print('不能取消{}'.format(i))
  • 异常处理
from concurrent import futures

def task():
    print('任务开始')
    raise ValueError('任务执行出错了')

if __name__ == '__main__':
    ex = futures.ThreadPoolExecutor(max_workers=2)
    print('程序开始...')
    f = ex.submit(task)

    # 使用exception()获取异常信息
    error = f.exception()
    print('错误: error:{}'.format(error))

    # 使用result()获取执行结果会出现异常
    # 也可以使用try...except获取异常
    try:
        result = f.result()
    except ValueError as e:
        print('访问异常 {}'.format(e))

6、协程的应用

  • 一个线程是调用某个函数方法,协程可以控制函数方法的执行过程,转向执行其他函数方法,并在适当的时候切换到原来的函数方法中继续执行
  • Python中常见的协程模块有yield、yield from、async/await、asyncio、Gevent等,只有Gevent是第三方模块,其他都是Python的内置模块

6.1 yield与yield from

  • yield实现协程功能
def consumer():
    """任务1:接收数据,处理数据"""
    print('开始接收数据')
    while True:
        print('等待中……')
        x = yield
        print('处理数据:', x)

def producer():
    """任务2:生产数据"""
    c = consumer()
    # 找到函数consumer()的yield位置
    next(c)
    for i in range(2):
        print('发送数据:', i)
        # 给函数consumer()的yield传值,然后循环给下一个yield传值
        # 使用yield的send()方法切换另一个任务
        c.send(i)

if __name__ == '__main__':
    # 基于yield保存状态,实现两个任务直接来回切换,即并发的效果
    producer()
  • yield from重要的作用是提供了一个数据传输管道
def consumer():
    print('开始接收数据')
    while True:
        print('等待中……')
        x = yield
        print('处理数据:', x)

def wraps(c):
    while True:
        print('running')
        yield from c

def producer(wrap):
    next(wrap)
    for i in range(2):
        print('发送数据:', i)
        wrap.send(i)

if __name__ == '__main__':
    c = consumer()
    wrap = wraps(c)
    producer(wrap)

6.2 asyncio与async/await

  • asyncio是Python 3.4版本引入的标准模块,直接内置了对异步IO的支持
import asyncio

@asyncio.coroutine
def say_hello():
    print("Hello Python!")
    # 调用asyncio.sleep(1),设置延时
    # yield from是协程切换执行的函数
    yield from asyncio.sleep(1)
    print("Hello Django!")

# 创建EventLoop对象
loop = asyncio.get_event_loop()
# 执行异步函数say_hello()
loop.run_until_complete(say_hello())
# 执行多个异步函数
# loop.run_until_complete(asyncio.wait([say_hello(), say_hello()]))
# 销毁EventLoop对象
loop.close()
  • asyncio的使用过程如下:
    (1)使用@asyncio.coroutine作为函数的装饰器,使函数变为异步函数。
    (2)调用get_event_loop()方法创建EventLoop对象。
    (3)由EventLoop对象调用run_until_complete()方法执行异步函数say_hello()。
    (4)如果需要同时执行多个异步函数,那么将多个函数以列表方式表示,并作为asyncio.wait()的函数参数,最后由run_until_complete()方法执行。
    (5)由EventLoop对象调用close()方法销毁EventLoop对象。

  • 关键字async/await是从Python 3.5开始引入的新语法,它主要优化asyncio.coroutine的语法

import asyncio
import time

async def get_times():
    for i in range(2):
        print('进入等待')
        # 设置延时
        await asyncio.sleep(1)
        print('Hello times is ', i)

async def say_hello():
    print("Hello Python!")
    # 调用异步函数get_times()
    await get_times()
    print("Hello Django!")

start = time.time()
# 创建EventLoop对象
loop = asyncio.get_event_loop()
# 执行异步函数say_hello()
# loop.run_until_complete(say_hello())
# 执行多个异步函数
loop.run_until_complete(asyncio.wait([say_hello(), say_hello()]))
# 销毁EventLoop对象
loop.close()
end = time.time()
print('一共耗时:', end-start)
  • 上述代码的修改说明如下:
    (1)将代码原有的@asyncio.coroutine和yield from分别改为async和await,并且还定义了异步函数get_times()。
    (2)在异步函数say_hello()中,使用关键字await调用异步函数get_times()。
    (3)程序在执行异步函数get_times()的时候,当出现延时asyncio.sleep(1)时,程序自动切换并执行另一个异步任务。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值