python网络编程(5)—— 进程

python网络编程(5)—— 进程

  • 进程
    • 进程的状态
  • 进程、线程对比
    • 定义对比
    • 与线程的不同
    • 优缺点
  • 进程的创建
    • 给子进程指定的函数传递参数
    • 注意事项
      • 1、进程之间是不共享变量的
    • 2、进程间通信
  • 进程池
  • 多进程拷贝文件实例

进程

程序:例如xxx.py这是程序,是一个静态的

进程:一个程序运行起来后,代码+用到的资源 称之为进程,它是操作系统分配资源的基本单元。

不仅可以通过线程完成多任务,进程也可以。

进程的状态

  • 就绪态:运行的条件都已经慢去,正在等在cpu执行
  • 执行态:cpu正在执行其功能
  • 等待态:等待某些条件满足,例如一个程序sleep了,此时就处于等待态

进程、线程对比

定义对比

  • 进程是系统进行资源分配和调度的一个独立单位。
  • 线程是进程的一个实体,,是cpu调度和分派的基本单位,它比进程更小,能独立运行,线程基本不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器、一组寄存器和栈),但是他与同属一个进程的其他线程共享进程所拥有的全部资源。

与线程的不同

  • 一个程序至少有一个进程、一个进程至少有一个线程。
  • 线程的划分尺度小于进程(资源比进程少),是的多线程程序的并发性高。
  • 进程在执行过程中拥有独立的内存单元、而多个线程共享内存,从而极大地提高了程序的运行效率。
  • 线程不能独立运行,必须依存在进程中。
  • 可以将进程理解为工厂中的一条流水线,而其中的线程就是这个流水线上的工人

优缺点

线程和进程在使用上各有优缺点:线程执行开销小,但不利于资源的管理和保护;而进程正相反。

进程的创建

multiprocessing模块就是跨平台版本的多进程模块,提供了一个Process类来代表一个进程对象,这个对象可以理解为是一个独立的进程,可以执行另外的事情。

def sing():
    print(f'开始唱歌。。。{ctime()}')
    sleep(3)
    print(f'结束唱歌。。。{ctime()}')


def dance():
    print(f'开始跳舞。。。{ctime()}')
    sleep(3)
    print(f'结束跳舞。。。{ctime()}')

if __name__ == '__main__':
    print(f'程序开始--------------{ctime()},pid={os.getpgid()},ppid={os.getppid()}')
    p1 = multiprocessing.Process(target=sing)
    p2 = multiprocessing.Process(target=dance)
    p1.start()
    p2.start()
    p1.join()
    print(f'程序结束-------------{ctime()},pid={os.getpgid()},ppid={os.getppid()}')

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

  • target:如果传递了函数的引用,可以任务这个子进程就执行这里的代码
  • args:给target指定的函数传递的参数,以元组的方式传递
  • kwargs:给target指定的函数传递命名参数
  • name:给进程设定一个名字,可以不设定
  • group:指定进程组,大多数情况下用不到

Process创建的实例对象的常用方法:

  • start():启动子进程实例(创建子进程)
  • is_alive():判断进程子进程是否还在活着
  • join([timeout]):是否等待子进程执行结束,或等待多少秒
  • terminate():不管任务是否完成,立即终止子进程

Process创建的实例对象的常用属性:

  • name:当前进程的别名,默认为Process-N,N为从1开始递增的整数
  • pid:当前进程的pid(进程号)

给子进程指定的函数传递参数

def func(name,age,**kwargs):
    for i in range(10):
        print(f'子进程运行。。name={name},age={age},pid={os.getpid()}')
        print(kwargs)
        i += 1
        print('次数', i)
        time.sleep(0.2)


if __name__ == '__main__':
    p = multiprocessing.Process(target=func,args=('小埋',18),kwargs={
     'home':'广州'})
    p.start()
    time.sleep(2)
    p.terminate()# 立即结束此进程

注意事项

1、进程之间是不共享变量的

g_lis = [11,22,33]
def work1():
    g_lis.append(44)
    print(f'--------work1中,g_lis={g_lis}')
def work2():
    print(f'--------work2中,g_lis={g_lis}')
if __name__ == '__main__':
    print(f'子进程创建之前,g_lis:{g_lis}')
    p1 = multiprocessing.Process(target=work1)
    p1.start()
    p1.join()
    p2 = multiprocessing.Process(target=work2)
    p2.start()

2、进程间通信

q = multiprocessing.Queue(3) # 初始化一个Queue对象,最多可以接收3条消息
q.put('消息1')
q.put('消息2')
print(q.full())
q.put('消息3')
print(q.full())
# try:
#     q.put('消息',True,2)
# except Exception as e:
#     print(e)

if not q.empty(): # 也可以用while循环
    for i in range(q.qsize()):
        str1 = q.get()
        print(str1)

如果设置超时时间,在放入信息时,会等待相应的时间,
如结束时还没有空间,则抛出异常。
如果第二个参数(block)为False,队列没有空间,则立即抛出异常。

如果第二个参数(block)为默认值(True)且没有设置超时时间,消息会阻塞,
直到有空间。

def consumer(q,name):
    while True:
        food = q.get()
        if not food:
            print('已经吃完了。。。')
            break
        print(f'{name}消费了{food}')
        time.sleep(random.randint(0,5))
        q.task_done() # 向生产者发送信号,消费了Q队列里的一个信息

def producer(name,q,food):
    for i in range(10):
        f = f'{name}生产了{food}{i}'
        time.sleep(random.randint(0, 5))
        print(f)
        q.put(food)
    q.join() # 当生产者生产完毕之后,会阻塞,等消费者消费

if __name__ == '__main__':
    q = multiprocessing.JoinableQueue()
    p1 = multiprocessing.Process(target=producer,args=('娃哈哈',q,'包子'))
    p2 = multiprocessing.Process(target=producer,args=('旺仔',q,'馒头'))
    p1.start()
    p2.start()

    c1 = multiprocessing.Process(target=consumer,args=(q,'小埋'))
    c2 = multiprocessing.Process(target=consumer,args=(q,'巫马鹿'))
    c1.daemon = True
    c2.daemon = True
    c1.start()
    c2.start()

    # 主程序等待生产者全部生产完毕
    p1.join()
    p2.join()

    # 朝队列中放入两个指示性空值
    # q.put(None)
    # q.put(None)
    print('主程序结束')

如果某个进程的daemon属性为False,主进程结束时,会检测该进程,是否结束。如果该进程还在运行,主进程会等待它完成之后再弹出
如果某个进程的daemon属性为True,主进程结束时,不会检测该进程是否结束,而直接结束,主进程会等待它完成之后再弹出,并且所有daemon属性为True的进程会直接关闭,不论是否运行完毕。

进程池

除了使用Process创建进程外,还可以使用进程池Pool创建,前者适用于处理少量进程,后者适用于处理大量进程,免去编写过多进程的苦恼。
顺带提一下Pipe()管道。

def read(p1):
    print(f'read启动pid={os.getpid()}')
    print(f'read读取到{p1.recv()}') # recv从管道中读取数据

def write(p2,i):
    print(f'write启动pid={os.getpid()}')
    p2.send(f'{i}python') # 向管道中发送数据
    print(f'放入消息{i}python')


if __name__ == '__main__':
    po = Pool()
    p1,p2 = Pipe() # 生产一个管道,一段可以放入数据,一段可以读取数据
    for i in range(3):
        po.apply_async(read,(p1,))
    for i in range(3):
        po.apply_async(write,(p2,i))

    po.close()
    po.join()

多进程拷贝文件实例

def copy_file(p1,file_name,source_f,dest_f):
    with open(source_f+'/' + file_name,'rb') as fr,open(dest_f + '/' + file_name,'wb') as fw:
        while True:
            content = fr.read(1024) # 读取原文件
            fw.write(content) # 写入新文件
            if len(content) < 1024:
                break
    p1.send(file_name) # 将已备份好的文件名放入管道
def main():
    source_f = input('请输入要备份的目录名')
    # 生成新的目录名
    dest_f = source_f+'[备份]'
    if not os.path.exists(dest_f):
        # 创建目录
        os.mkdir(dest_f)
    file_name_list = os.listdir(source_f) # 获取原目录中所有文件名
    p1,p2 = multiprocessing.Pipe()# 创建管道
    pool = multiprocessing.Pool(3) # 创建进程池
    for file_name in file_name_list:
        pool.apply_async(copy_file,(p1,file_name,source_f,dest_f))
    pool.close() # 不再添加任务
    all_num = len(file_name_list)
    while 1:
        # 获取已经完成的文件

        file_name = p2.recv()
        if file_name in file_name_list:
            file_name_list.remove(file_name)
        # 进度
        copy_rate = (all_num - len(file_name_list)) / all_num
        print(f'复制进度{file_name}{copy_rate:.0%}')
        # 如果进度达到百分百,则跳出
        if copy_rate >= 1:
            break

if __name__ == '__main__':
    main()

你可能感兴趣的:(python新手入门,多进程,python)