17. python从入门到精通——使用进程和线程

目录

四种创建进程方式

multiprocessing模块中的Process类创建进程

Process类的子类创建进程

使用multiprocessing模块中的进程池Pool类创建进程

进程间通信

multiprocessing多进程模块中Queue类用于实现队列相关操作

常用操作

使用队列进程间通信

线程

创建线程

threading模块Thread类创建线程        

使用Thread子类创建线程

线程间通信

互斥锁的使用       

使用队列在线程间通信


python中使用进程:在python中调用相应的模块,然后通过这个模块操作操作系统中的进程

四种创建进程方式

  1. os.fork()函数:不支持windos
  2. multiprocessing多进程模块Process类
  3. Process类的子类
  4. Pool进程池

multiprocessing模块中的Process类创建进程

用法:

Process([group [, target [, name [, args [, kwargs]]]]])
    #group:参数未使用,值始终为None。
    #target:表示当前进程启动时执行的可调用对象。
    #name:为当前进程实例的别名。
    #args:表示传递给target函数的参数元组。
    #kwargs:表示传递给target函数的参数字典。

Process类中常用方法和属性:

    方法:

          is_alive():判断进程实例是否还在执行;
          join([timeout]):是否等待进程实例执行结束,或等待多少秒;
          start():启动进程实例(创建子进程);
          run():如果没有给定target参数,对这个对象调用start()方法时,就将执行对象中的run()方法;
          terminate():不管任务是否完成,立即终止;

    属性:

          name:当前进程实例别名,默认为Process-N,N为从1开始递增的整数;
          pid:当前进程实例的PID值。

实例1

注:以下程序执行后得到的结果是主进程先执行完,由于子进程睡眠1秒所以最后执行完说明主进程和子进程是同步进行的

from multiprocessing import Process
import time


def test(intervel):
    time.sleep(intervel)  # 睡眠
    print("我是子进程")


def main():
    print('主程序开始')
    p = Process(target=test, args=(1,))  # 设置要调用的子进程和传递参数1让子进程睡眠1秒
    p.start()
    print('主进程结束')


if __name__ == '__main__':
    main()


#主程序开始
#主进程结束
#我是子进程

实例2:

# -*- coding:utf-8 -*-
from multiprocessing import Process
import time
import os

#两个子进程将会调用的两个方法
def  child_1(interval):
    print("子进程(%s)开始执行,父进程为(%s)" % (os.getpid(), os.getppid()))
    t_start = time.time()   # 计时开始
    time.sleep(interval)    # 程序将会被挂起interval秒
    t_end = time.time()     # 计时结束
    print("子进程(%s)执行时间为'%0.2f'秒"%(os.getpid(),t_end - t_start))

def  child_2(interval):
    print("子进程(%s)开始执行,父进程为(%s)" % (os.getpid(), os.getppid()))
    t_start = time.time()   # 计时开始
    time.sleep(interval)    # 程序将会被挂起interval秒
    t_end = time.time()     # 计时结束
    print("子进程(%s)执行时间为'%0.2f'秒"%(os.getpid(),t_end - t_start))

if __name__ == '__main__':
    print("------父进程开始执行-------")
    print("父进程PID:%s" % os.getpid())                  # 输出当前程序的ID
    p1=Process(target=child_1,args=(1,))                   # 实例化进程p1
    p2=Process(target=child_2,name="mrsoft",args=(2,))     # 实例化进程p2
    p1.start()  # 启动进程p1
    p2.start()  # 启动进程p2
    #同时父进程仍然往下执行,如果p2进程还在执行,将会返回True
    print("p1.is_alive=%s"%p1.is_alive())
    print("p2.is_alive=%s"%p2.is_alive())
    #输出p1和p2进程的别名和pid
    print("p1.name=%s"%p1.name)
    print("p1.pid=%s"%p1.pid)
    print("p2.name=%s"%p2.name)
    print("p2.pid=%s"%p2.pid)
    print("------等待子进程-------")
    p1.join() # 等待p1进程结束
    p2.join() # 等待p2进程结束
    print("------父进程执行结束-------")
###########################################
##------父进程开始执行-------
##父进程PID:13668
##p1.is_alive=True
##p2.is_alive=True
##p1.name=Process-1
##p1.pid=16536
##p2.name=mrsoft
##p2.pid=11176
##------等待子进程-------
##子进程(16536)开始执行,父进程为(13668)
##子进程(11176)开始执行,父进程为(13668)
##子进程(16536)执行时间为'1.01'秒
##子进程(11176)执行时间为'2.00'秒
##------父进程执行结束-------

Process类的子类创建进程

对于一些简单的小任务可以使用multiprocessing模块中的Precess类创建多个子进程;但是进程中药处理复杂任务时我们通常会定义一个Process类,然后让其他n个子类继承这个Process类,这样就等同于实例化了n个进程

实例:使用Process子类创建两个子进程,并记录子进程运行时间

# -*- coding:utf-8 -*-
from multiprocessing import Process
import time
import os

#继承Process类
class SubProcess(Process):
    # 由于Process类本身也有__init__初始化方法,这个子类相当于重写了父类的这个方法
    def __init__(self,interval,name=''):
        Process.__init__(self)      # 调用Process父类的初始化方法
        self.interval = interval    # 接收参数interval
        if name:                    # 判断传递的参数name是否存在
            self.name = name        # 如果传递参数name,则为子进程创建name属性,否则使用默认属性
    #重写了Process类的run()方法
    def run(self):
        print("子进程(%s) 开始执行,父进程为(%s)"%(os.getpid(),os.getppid()))
        t_start = time.time()
        time.sleep(self.interval)
        t_stop = time.time()
        print("子进程(%s)执行结束,耗时%0.2f秒"%(os.getpid(),t_stop-t_start))

if __name__=="__main__":
    print("------父进程开始执行-------")
    print("父进程PID:%s" % os.getpid())                  # 输出当前程序的ID
    p1 = SubProcess(interval=1,name='mrsoft')
    p2 = SubProcess(interval=2)
    #对一个不包含target属性的Process类执行start()方法,就会运行这个类中的run()方法,所以这里会执行p1.run()
    p1.start()  # 启动进程p1
    p2.start()  # 启动进程p2
    # 输出p1和p2进程的执行状态,如果真正进行,返回True,否则返回False
    print("p1.is_alive=%s"%p1.is_alive())
    print("p2.is_alive=%s"%p2.is_alive())
    #输出p1和p2进程的别名和pid
    print("p1.name=%s"%p1.name)
    print("p1.pid=%s"%p1.pid)
    print("p2.name=%s"%p2.name)
    print("p2.pid=%s"%p2.pid)
    print("------等待子进程-------")
    p1.join() # 等待p1进程结束
    p2.join() # 等待p2进程结束
    print("------父进程执行结束-------")
##############################
##------父进程开始执行-------
##父进程PID:17252
##p1.is_alive=True
##p2.is_alive=True
##p1.name=mrsoft
##p1.pid=19728
##p2.name=SubProcess-2
##p2.pid=18436
##------等待子进程-------
##子进程(19728) 开始执行,父进程为(17252)
##子进程(18436) 开始执行,父进程为(17252)
##子进程(19728)执行结束,耗时1.00秒
##子进程(18436)执行结束,耗时2.01秒
##------父进程执行结束-------

使用multiprocessing模块中的进程池Pool类创建进程

优势:比Process类创建进程节约资源

Pool类的常用方法:

        apply_async(func[, args[, kwds]]) :使用非阻塞方式调用func函数(并行执行,堵塞方式必须等待上一个进程退出才能执行下一个进程),func为函数名称;args为传递给func的参数是个元组,kwds为传递给func的字典。
        apply(func[, args[, kwds]]):使用阻塞方式调用func函数。
        close():关闭Pool,使其不再接受新的任务。
        terminate():不管任务是否完成,立即终止。
        join():主进程阻塞,等待子进程的退出, 必须在close或terminate之后使用。

实例:使用进程池创建子进程

# -*- coding=utf-8 -*-
from multiprocessing import Pool
import os, time

def task(name):
    print('子进程(%s)执行task %s ...' % ( os.getpid() ,name))
    time.sleep(1)       # 休眠1秒

if __name__=='__main__':
    print('父进程(%s).' % os.getpid())
    p = Pool(3) #进程池设置最多有三个进程   
    for i in range(10):                 # 从0开始循环10次    
        p.apply_async(task, args=(i,))  # 使用非阻塞方式调用task()函数   
    print('等待所有子进程结束...')
    p.close()   # 关闭进程池,关闭后p不再接收新的请求
    p.join()    # 等待子进程结束
    print('所有子进程结束.')
########################################
##父进程(16252).
##等待所有子进程结束...
##子进程(11076)执行task 0 ...
##子进程(17112)执行task 1 ...
##子进程(17636)执行task 2 ...
##子进程(11076)执行task 3 ...
##子进程(17112)执行task 4 ...
##子进程(17636)执行task 5 ...
##子进程(11076)执行task 6 ...
##子进程(17112)执行task 7 ...
##子进程(17636)执行task 8 ...
##子进程(11076)执行task 9 ...
##所有子进程结束.

进程间通信

实例:验证子进程之间是否共用变量信息和直接进行通信

from multiprocessing import Process

def plus():
    print("--子进程1开始--")
    global g_num
    g_num += 50
    print('g_num is %d' % g_num)
    print("--子进程结束--")

def minus():
    print("--子进程2开始--")
    global g_num
    g_num -= 50
    print('g_num is %d' % g_num)
    print("--子进程结束--")

g_num = 100
if __name__ == '__main__':
    print("--主进程开始--")
    p1 = Process(target=plus)
    p2 = Process(target=minus)
    p1.start()
    p2.start()
    p1.join()
    p2.join()
    print("--主进程结束--")

######################################################
##--主进程开始--
##--子进程1开始--
##g_num is 150  
##--子进程结束--
##--子进程2开始--
##g_num is 50   
##--子进程结束--
##--主进程结束--

        结果:子进程之间无法公用变量信息和直接进行通信

        结论:主进程与子进程、子进程与子进程之间无法直接进行通信,这是因为每个进程都有自己的地址空间、内存、数据栈以及其他记录其状态的辅助数据

进程间通信方法:队列(Queue)和管道(Pipe)

        队列:在一个队列中遵循先进先出原则

multiprocessing多进程模块中Queue类用于实现队列相关操作

常用操作

Queue.qsize():返回当前队列包含的消息数量。
Queue.empty():如果队列为空,返回True,反之返回False 。
Queue.full():如果队列满了,返回True,反之返回False。
Queue.get([block[, timeout]]):获取队列中的一条消息,然后将其从列队中移除,block默认值为True。timeout为超时时间,如果队列为空时如果等待时间超过timeout则抛出异常
Queue.get_nowait():相当Queue.get(False),如果队列为空时立即抛出异常
Queue.put(item,[block[, timeout]]):将item消息写入队列,block默认值为True。
Queue.put_nowait(item):相当Queue.put(item, False)。

示例:

from multiprocessing import Queue

if __name__ == '__main__':
    q = Queue(3) #列队中最多有3条信息,默认为无限长
    q.put("消息1")
    q.put("消息2")
    print('列队是否已满:%s' % q.full())
    q.put("消息3")
    print('列队是否已满:%s' % q.full())
    try:
        q.put("消息4",True,2) #测试写入第4条消息等待2秒后异常
    except:
        print("消息列队已满,现有消息数量%s" % q.qsize())

    if not q.empty():
        print("--从列队中取消息--")
        for i in  range(q.qsize()):
            print(q.get_nowait())
    if not q.full():
        q.put("消息2")
        print(q.qsize())

#######################################
##列队是否已满:False
##列队是否已满:True
##消息列队已满,现有消息数量3
##--从列队中取消息--
##消息1
##消息2
##消息3
##1

使用队列进程间通信

示例:分别向队列中写入和读取数据

        #创建两个进程,一个向队列中写入数据,一个向队列中读取数据

# -*- coding: utf-8 -*-
from multiprocessing import Process, Queue

# 向队列中写入数据
def write_task(q):
    if not q.full():
        for i in range(5):
            message = "消息" + str(i)
            q.put(message)
            print("写入:%s"%message)
# 从队列读取数据
def read_task(q):
    while not q.empty():
        print("读取:%s" % q.get(True,2)) # 等待2秒,如果还没读取到任何消息,则抛出"Queue.Empty"异常

if __name__ == "__main__":
    print("-----父进程开始-----")
    q = Queue()  # 父进程创建Queue,并传给各个子进程
    pw = Process(target=write_task, args=(q,))      # 实例化写入队列的子进程,并且传递队列
    pr = Process(target=read_task, args=(q,))       # 实例化读取队列的子进程,并且传递队列
    pw.start()   # 启动子进程 pw,写入
    pr.start()   # 启动子进程 pr,读取
    pw.join()    # 等待 pw 结束
    pr.join()    # 等待 pr 结束
    print("-----父进程结束-----")
####################################################
##-----父进程开始-----
##写入:消息0
##写入:消息1
##写入:消息2
##写入:消息3
##写入:消息4
##读取:消息0
##读取:消息1
##读取:消息2
##读取:消息3
##读取:消息4
##-----父进程结束-----

线程

线程:操作系统能够进行运算调度的最小单位,它被包含在进程之中,是进程中的实际运作单位,在一个进程中可以并发多个线程,每条线程可以执行不同的任务

进程与线程的根本区别与关系

        进程是操作系统资源分配的最小单位,而线程是任务调度和执行的最小单位

        一个进程可以有多个线程

        线程共享进程资源

创建线程

有两种方法:

        threading模块Thread类创建线程

        使用Thread子类创建线程

语法:

        Thread([group [, target [, name [, args [, kwargs]]]]])

            #group:参数未使用,值始终为None。
            #target:表示当前线程启动时执行的可调用对象。
            #name:为当前线程实例的别名。
            #args:表示传递给target函数的参数元组。
            #kwargs:表示传递给target函数的参数字典。

threading模块Thread类创建线程        

示例:

import threading
import time
def thread():
    for i in range(3):
        time.sleep(1)
        print("thread name is %s" % threading.current_thread().name)

if __name__ == '__main__':
    print("--主线程开始--")
    threads = [threading.Thread(target = thread) for i in range(4)]
    for t in  threads:
        t.start()
    for t in threads:
        t.join()
    print("--主进程结束--")

#############################################
##--主线程开始--
##thread name is Thread-2 (thread)
##thread name is Thread-3 (thread)
##thread name is Thread-1 (thread)
##thread name is Thread-4 (thread)
##thread name is Thread-4 (thread)
##thread name is Thread-1 (thread)
##thread name is Thread-2 (thread)
##thread name is Thread-3 (thread)
##thread name is Thread-3 (thread)
##thread name is Thread-2 (thread)
##thread name is Thread-4 (thread)
##thread name is Thread-1 (thread)
##--主进程结束--  

使用Thread子类创建线程

实例:

# -*- coding: utf-8 -*-
import threading
import time
class SubThread(threading.Thread):
    def run(self):
        for i in range(3):
            time.sleep(1)
            msg = "子线程"+self.name+'执行,i='+str(i) #name属性中保存的是当前线程的名字
            print(msg)
if __name__ == '__main__':
    print('-----主线程开始-----')
    t1 = SubThread() # 创建子线程t1
    t2 = SubThread() # 创建子线程t2
    t1.start()      # 启动子线程t1
    t2.start()      # 启动子线程t2
    t1.join()       # 等待子线程t1
    t2.join()       # 等待子线程t2
    print('-----主线程结束-----')
#########################################
##-----主线程开始-----
##子线程Thread-2执行,i=0
##子线程Thread-1执行,i=0
##子线程Thread-2执行,i=1
##子线程Thread-1执行,i=1
##子线程Thread-1执行,i=2
##子线程Thread-2执行,i=2
##-----主线程结束-----  

线程间通信

示例:测试线程间通信

from threading import Thread

def plus():
    print("--子线程1开始--")
    global g_num
    g_num += 50
    print('g_num is %d' % g_num)
    print("--子线程结束--")

def minus():
    print("--子线程2开始--")
    global g_num
    g_num -= 50
    print('g_num is %d' % g_num)
    print("--子线程结束--")

g_num = 100
if __name__ == '__main__':
    print("--主进程开始--")
    t1 = Thread(target=plus)
    t2 = Thread(target=minus)
    t1.start()
    t2.start()
    t1.join()
    t2.join()
    print("--主进程结束--")
######################################
##--主进程开始--
##--子线程1开始--
##g_num is 150   
##--子线程2开始--
##g_num is 100   
##--子线程结束-- 
##--子线程结束-- 
##--主进程结束-- 

以上程序表明同一个进程中多个线程可共享数据,但是当多个线程共同使用一个变量时也会造成混乱此时是不安全的线程

加锁:在python中为了实现安全的线程使用了给线程加锁的方式,通过加锁和解锁的方式保证了线程的安全,又叫互斥锁

互斥锁:使用互斥锁可以防止多个线程同时读写某一个内存的区域,互斥锁为资源引入一个状态:要么是锁定,要么是非锁定。当某个线程要更改共享数据的时候需要先将其锁定,此时资源的状态就为锁定,其他线程则不能更改直到该线程释放了这个资源也就是解锁以后将资源的状态变为非锁定。此时其他的线程才可以再次锁定该资源。互斥锁保证了每次只有一个线程进行读写操作从而保证了多线程情况下数据的正确性

互斥锁的使用       

创建锁: mutex = threading.Lock()

锁定:metex.acquire([blocking])

                #blocking:是一个bool值如归将它设置为False,当无法获取锁定时将 立即返回False,如果成功获取锁定则返回True  

释放锁:metex.release() #如果在没有锁定时调用释放锁则会抛出异常

实例:通过多线程和互斥锁实现多人订购电影票功能

from threading import Thread,Lock
import time
n=100 # 共100张票                      

def task():
    global n
    mutex.acquire()             # 上锁
    temp=n                      # 赋值给临时变量
    time.sleep(0.1)             # 休眠0.1秒
    n=temp-1                    # 数量减1 
    print('购买成功,剩余%d张电影票'%n)
    mutex.release()             # 释放锁

if __name__ == '__main__':
    mutex=Lock()                # 实例化Lock类
    t_l=[]                      # 初始化一个列表
    for i in range(10):         
        t=Thread(target=task)   # 实例化线程类    
        t_l.append(t)           # 将线程实例存入列表中
        t.start()               # 创建线程
    for t in t_l:               
        t.join()                # 等待子线程结束    
#######################################
##购买成功,剩余99张电影票
##购买成功,剩余98张电影票
##购买成功,剩余97张电影票
##购买成功,剩余96张电影票
##购买成功,剩余95张电影票
##购买成功,剩余94张电影票
##购买成功,剩余93张电影票
##购买成功,剩余92张电影票
##购买成功,剩余91张电影票
##购买成功,剩余90张电影票

注意死锁问题:两个线程同时在等待对方释放相应资源而造成死锁现象

使用队列在线程间通信

使用队列模拟生产者与消费者模式

        注意:进程列队需要从queue模块中导入Queue类

# -*- coding: utf-8 -*-
from queue import Queue
import random,threading,time

#生产者类
class Producer(threading.Thread):
    def __init__(self, name,queue):
        threading.Thread.__init__(self, name=name)
        self.data=queue
    def run(self):
        for i in range(5):
            print("生产者%s将产品%d加入队列!" % (self.getName(), i))
            self.data.put(i)
            time.sleep(random.random())
        print("生产者%s完成!" % self.getName())

#消费者类
class Consumer(threading.Thread):
    def __init__(self,name,queue):
        threading.Thread.__init__(self,name=name)
        self.data=queue
    def run(self):
        for i in range(5):
            val = self.data.get()
            print("消费者%s将产品%d从队列中取出!" % (self.getName(),val))
            time.sleep(random.random())
        print("消费者%s完成!" % self.getName())

if __name__ == '__main__':
    print('-----主线程开始-----')
    queue = Queue()        # 实例化队列
    producer = Producer('Producer',queue)   # 实例化线程Producer,并传入队列作为参数
    consumer = Consumer('Consumer',queue)   # 实例化线程Consumer,并传入队列作为参数
    producer.start()    # 启动线程Producer
    consumer.start()    # 启动线程Consumer
    producer.join()     # 等待线程Producer结束
    consumer.join()     # 等待线程Consumer结束
    print('-----主线程结束-----')
#################################################################
##-----主线程开始-----
##生产者Producer将产品0加入队列!
##消费者Consumer将产品0从队列中取出!
##生产者Producer将产品1加入队列!
##生产者Producer将产品2加入队列!
##消费者Consumer将产品1从队列中取出!
##生产者Producer将产品3加入队列!
##消费者Consumer将产品2从队列中取出!
##消费者Consumer将产品3从队列中取出!
##生产者Producer将产品4加入队列!
##生产者Producer完成!
##消费者Consumer将产品4从队列中取出!
##消费者Consumer完成!
##-----主线程结束-----
            

​​​​​​​

猜你喜欢

转载自blog.csdn.net/weixin_43812198/article/details/131853141