Python基础学习笔记8

文章目录

  • 一、程序与进程
    • 1.1 创建进程的方式--multiprocssing
    • 1.2 Process类常用的方法和属性
    • 1.3 创建进程的方式
      • 1.3.1 multiprocessing模块Process子类创建进程
      • 1.3.2 Pool进程池
  • 二、并发和并行
  • 三、进程之间的通信
    • 3.1 队列的基本使用
    • 3.2 队列实现进程之间的通信
  • 四、线程
    • 4.1 创建线程的方式
  • 五、线程之间的通信
  • 六、线程操作共享数据的安全性问题
    • 6.1 数据错乱
    • 6.2 锁机制
  • 七、生产者模式和消费者模式
    • 7.1 队列实现线程之间的通信

一、程序与进程

  • 程序
    -Program,一系列指令的集合
    -使用编程语言所编写的指令的集合,用于实现一定的功能

  • 进程
    -启动后的程序在系统中就有一个进程,系统会为这个进程分配内存空间

1.1 创建进程的方式–multiprocssing

  • os.fork()函数
    -只适用于Unix、Linux、Mac操作系统

  • multiprocessing模块
    -Process(group=None,target,name,args,kwargs)

  • Pool进程池

import time
from multiprocessing import Process
import os

def tast():
    print(f'我是子进程,我的PID是:{os.getpid()},我的父进程是:{os.getppid()}')
    time.sleep(3)

if __name__ == '__main__':
    start = time.time() #开始时间
    lst = []
    print('主进程开始执行:')
    #创建5个子进程
    for i in range(5):
        #创建1个子进程对象
        pro = Process(target=tast)
        #启动进程
        pro.start()
        #启动的进程添加到列表中
        lst.append(pro)
    #列表中的每个成员都是一个进程 将子进程加入列表,运行时间变快
    for item in lst:
        item.join() #阻塞主进程
        
    print(f'运行5个子进程一共花了多长时间:{time.time()-start}')
    print('主进程执行结束') #只是主进程中没有代码了,但并不是主进程结束
    #主进程要等到所有的子进程结束之后才会结束

1.2 Process类常用的方法和属性

Python基础学习笔记8_第1张图片
Python基础学习笔记8_第2张图片

import os
import time
from multiprocessing import Process


def sub_process1(name):
    print(f'子进程PID:{os.getpid()},父进程的PID:{os.getppid()},---------------------{name}')
    time.sleep(1)

def sub_process2(age):
    print(f'子进程PID:{os.getpid()},父进程的PID:{os.getppid()},---------------------{age}')
    time.sleep(1)

if __name__ == '__main__':
    print('父进程开始执行')
    for i in range(5):
        #创建子进程1
        p1 = Process(target=sub_process1,args=('jww',))
        #创建子进程2
        p2 = Process(target=sub_process2,args=(18,))
        #启动子进程
        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() #父进程等待p1执行结束
        p2.join() #父进程等待p2执行结束
    print('父进程执行结束')

#多个进程之间同时执行的顺序是随机的,不会根据创建的顺序执行
import os
import time
from multiprocessing import Process


def sub_process1(name):
    print(f'子进程PID:{os.getpid()},父进程的PID:{os.getppid()},---------------------{name}')
    time.sleep(1)

def sub_process2(age):
    print(f'子进程PID:{os.getpid()},父进程的PID:{os.getppid()},---------------------{age}')
    time.sleep(1)

if __name__ == '__main__':
    print('父进程开始执行')
    for i in range(5):
        # p1 = Process() #没有给定target参数,默认调用run方法
        # p2 = Process()
        # p1.start() #自动调用run方法执行
        # p2.start()
        # p1.run()
        # p2.run()
        p1 = Process(target=sub_process1,args=('jww',))
        p2 = Process(target=sub_process2,args=(18,))
        p1.start()
        p2.start()
        #终止进程
        p1.terminate()
        p2.terminate()
    print('父进程执行结束')

1.3 创建进程的方式

1.3.1 multiprocessing模块Process子类创建进程

  • multiprocessing模块Process子类创建进程
    -自定义类Process
    -重写run方法
import os
from multiprocessing import Process
#自定义类
class SubProcess(Process):
    #初始化的方法
    def __init__(self,name):
        #调用父类Process的初始化方法
        Process.__init__(self)
        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(1,6):
        p1 = SubProcess(f'进程--{i}')
        #启动进程
        p1.start()
        lst.append(p1)
    for item in lst:
        item.join()
    print('父进程执行结束')

1.3.2 Pool进程池

  • Pool进程池
    -p = Pool(N)
    Python基础学习笔记8_第3张图片
from multiprocessing import Pool
import time
import os
#编写任务
def task(name):
    print(f'子进程的PID:{os.getpid()},执行任务---task{name}')
    time.sleep(1)

if __name__ == '__main__':
    start = time.time()
    print('父进程开始执行')
    #创建一个进程池
    p = Pool(3) #进程池里,最大进程的个数

    #创建10个进程
    for i in range(10):
        #以非阻塞方式进行 
        # p.apply_async(func=task,args=(i,)) #穿插交替进行
        #以阻塞方式进行
        p.apply(func=task,args=(i,)) 
    p.close() #关闭进程,不再接收新任务
    p.join() #阻塞父进程,等待子进程结束
    print('所有子进程结束,父进程执行结束')
    print(time.time()-start)

二、并发和并行

  • 并发
    -两个或多个事件同一时间间隔发生,多个进程被交替轮换着执行
    Python基础学习笔记8_第4张图片
  • 并行
    -两个或多个事件在同一时刻发生,多条命令在同一时刻在多个处理器上同时执行
    Python基础学习笔记8_第5张图片

三、进程之间的通信

多个进程间的数据可以共享吗?——不可以
Python基础学习笔记8_第6张图片
全局变量a在父进程和两个子进程中各一份,各自操作各自a的值,对于a的计算结果并没有在进程之间传递,进程之间并没有共享数据

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 -= 50
    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进程执行结束
    p1.join()
    #等待p2进程执行结束
    p2.join()

    print('父进程执行结束')

Python基础学习笔记8_第7张图片

3.1 队列的基本使用

  • 队列(Queue)
    -先进先出
    Python基础学习笔记8_第8张图片
  • multiprocessing模块的Queue类
    -语法结构:q = Queue(N)
    Python基础学习笔记8_第9张图片
from multiprocessing import Queue
if __name__ == '__main__':
    #创建一个队列,最多可以接收3条消息
    q = Queue(3)
    print('队列是否为空:',q.empty())
    print('队列是否为满:',q.full())
    #向队列中添加消息
    q.put('hello') #block默认为True
    q.put('world')
    print('队列是否为空:', q.empty())
    print('队列是否为满:', q.full())
    q.put('python')
    print('队列是否为满:', q.full())
    print('队列中消息的个数:',q.qsize())
    # q.put('html') #队列已满,block=True默认为True,一直等待队列有空位置才会将html入队,然后继续执行
    # q.put('html',block=True,timeout=2) #等待2秒,若还没有空位置,抛出异常queue.Full
    # q.put_nowait('html') #相当于q.put('html',block=False),不等待,queue.Full
    print('-------------------------------------')
    #从队列中获取消息 出队的操作
    print(q.get())
    print('出队之后,消息的个数:',q.qsize())
    q.put_nowait('html') #入队
    print('入队之后,消息的个数:',q.qsize())
    #通过遍历出队所有元素
    if not q.empty():
        for i in range(q.qsize()):
            print(q.get_nowait())
    print('队列是否为空:', q.empty())
    print('队列是否为满:', q.full())
    print('队列中消息的个数:', q.qsize())

3.2 队列实现进程之间的通信

  • 一个进程负责向队列中写入数据
  • 一个进程负责从队列中读取数据
    Python基础学习笔记8_第10张图片
from multiprocessing import Process, Queue
import time
a = 100
#向队列中写入消息的进程要执行的函数
def write_msg(q):
    global a
    if not q.full():
        for i in range(1,6):
            a -= 10
            q.put(a) #入队操作,将a的值进行入队
            print('a入队时的值:',a)

#从队列中读取消息的进程要执行的函数
def read_msg(q):
    time.sleep(1)
    while not q.empty():
        print('出队a的值:',q.get())

if __name__ == '__main__':
    print('父进程开始执行')
    q = Queue() #由父进程创建队列,传给子进程,没有写个数,说明队列可接收的消息个数没有上限
    #创建两个子进程
    p1 = Process(target=write_msg,args=(q,))
    p2 = Process(target=read_msg,args=(q,))
    #启动两个子进程
    p1.start()
    p2.start()
    #等待写书进程结束
    p1.join()
    p2.join()

    print('----------------父进程执行结束------------------')

四、线程

  • 多任务的方式
    -一个应用程序内使用多个进程
    -一个进程内使用多个线程
  • 线程
    -CPU可调度的最小单位
    -被包含在进程中,是进程中实际的运作单位
    -一个进程中可以并发多个线程,每条线程并行执行不同的任务

4.1 创建线程的方式

  • 使用threading模块创建线程
  • 使用函数式创建线程
    -t = Thread(group,target,name,args,kwargs)
  • 使用Thread子类创建线程
    -自定义类继承Thread类
    -实现run方法

1. 函数方式创建线程

#多线程
import threading
from threading import Thread
import time
#编写函数
def tast():
    for i in range(3):
        time.sleep(1)
        print(f'线程{threading.current_thread().name}正在执行,{i}')

if __name__ == '__main__':
    print('主线程开始执行')
    start = time.time()
    # for i in range(2):
    #     t = Thread(target=tast)
    #     #启动线程
    #     t.start()
    #     t.join()
    lst = [Thread(target=tast) for i in range(2)] #放入列表,再启动,再join,可以提高程序运行效率
    for item in lst:
        item.start()
    for item in lst:
        item.join()
    print(f'共耗时{time.time()-start}')
    
    #多个线程执行是无序的,每次执行结果都不同

2. 继承方式创建线程

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的值为{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('主线程执行结束')

五、线程之间的通信

  • 一个进程内的所有线程共享全局变量
  • 不同的进程不会共享全局变量
    Python基础学习笔记8_第11张图片
from threading import Thread
import time
a = 100
def add():
    print('加的线程开始执行')
    global a
    a += 30
    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)
    sub = Thread(target=sub)
    #启动线程
    add.start()
    sub.start()
    add.join() #等待加的线程结束
    sub.join() #等待减的线程结束
    print('---------主线程结束---------')

Python基础学习笔记8_第12张图片

六、线程操作共享数据的安全性问题

6.1 数据错乱

-多线程都是在同一个进程中运行的,因此在进程中的全局变量所有线程都可以共享,这就造成了一个问题,因为线程的顺序是无序的,有可能会造成数据错乱

出现了几个线程卖同一张票的情况

import threading
from threading import Thread
import time
ticket = 50
def sale_ticket():
    global ticket
    for i in range(1000):
        if ticket>0:
            print(threading.current_thread().name + f'正在出售第{ticket}张票')
            ticket -= 1
        time.sleep(1)

if __name__ == '__main__':
    for i in range(3):
        t = Thread(target=sale_ticket)
        t.start()

Python基础学习笔记8_第13张图片

6.2 锁机制

-互斥锁的两种状态:锁定和非锁定
-当线程访问共享资源时,先将其“锁定“,其他线程不能修改;直到该线程释放资源时,将资源的状态变成”非锁定“时,其他线程才能操作共享资源

  • threading中的Lock类
    -创建锁对象:
    lock_obj = Lock()
    -Lock类的方法:
    1.上锁(锁定):obj.acquire()
    2.释放锁:obj.release()
    -使用锁的原则:
    1.把尽量少的和不耗时的代码放到锁中执行
    2.代码执行完成后要记得释放锁

解决方法

import threading
from threading import Thread,Lock
import time
ticket = 50
lock_obj = Lock() #创建锁对象
def sale_ticket():
    global ticket
    for i in range(1000):
        lock_obj.acquire()  # 加锁
        if ticket>0:
            print(threading.current_thread().name + f'正在出售第{ticket}张票')
            ticket -= 1
        time.sleep(1)
        lock_obj.release() #释放锁

if __name__ == '__main__':
    for i in range(3):
        t = Thread(target=sale_ticket)
        t.start()

带来的新问题:程序运行效率变低

七、生产者模式和消费者模式

  • 多个线程之间通信可以采用生产者模式和消费者模式
  • 生产者线程
    -生产者线程用于“生产”数据
  • 消费者线程
    -消费者线程用于“处理”数据

Python基础学习笔记8_第14张图片

7.1 队列实现线程之间的通信

  • Queue可以实现进程之间的通信,也可以实现线程之间的通信
  • 使用Queue实现生产者与消费者问题
    -生产者将“数据”放入队列
    -消费者从队列中“取出”数据
from queue import Queue #实现线程之间的通信
# from multiprocessing import Queue
from threading import Thread
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}')
            time.sleep(1)
        print('-----------消费者线程完成了所有数据的取出-----------')

if __name__ == '__main__':
    #创建队列
    queue = Queue()
    #创建生产者线程
    p = Producer('Produce',queue)
    #创建消费者线程
    con = Consumer('Consumer',queue)
    #启动线程
    p.start()
    con.start()
    #等待生产者线程结束,等待消费者线程结束
    p.join()
    con.join()
    print('主线程运行结束')

你可能感兴趣的:(Python基础学习,python,学习,linux)