多进程编程和多线程编程

多进程编程
forking工作原理
什么是forking
进程的生命周期
僵尸进程
forking编程
forking编程基本思路
解决zombie问题

多进程编程

forking工作原理

什么是forking

• fork(分岔)在Linux系统中使用非常广泛 
• 当某一命令执行时,父进程(当前进程)fork出一个子进程 
• 父进程将自身资源拷贝一份,命令在子进程中运行时,就具有和父进程完全一样的运行环境
• linux采用fork方式执行程序。当运行程序(命令)时,系统将会把父进程的资源拷贝一份,生成子进程。程序、命令在子进程中运行。当命令执行完毕后,子进程销毁。windows系统不支持多进程

执行shell的方式

• bash xxx.sh: 指定使用bash解释脚本,fork执行
• ./xxx.sh: 根据脚本第一行指定的解释器选择解释方法,fork执行
• source xxx.sh: 在当前进程中执行指令,不采用fork方式

进程的生命周期

• 父进程fork出子进程并挂起 
• 子进程运行完毕后,释放大部分资源并通知父进程,这个时候,子进程被称作僵尸进程 
• 父进程获知子进程结束,子进程所有资源释放

僵尸进程

• 僵尸进程没有任何可执行代码,也不能被调度 
• 如果系统中存在过多的僵尸进程,将因为没有可用的进程号而导致系统不能产生新的进程 
• 对于系统管理员来说,可以试图杀死其父进程或重启系统来消除僵尸进程
• 当子进程已经结束,但是父进程还未结束,父进程又没有处理僵尸进程的代码,僵尸进程就会一直存在,直到父进程工作结束。如果父进程结束了,子进程会变成孤儿进程,它将会被
• systemd接管。如果systemd发现子进程是僵尸进程,就会处理。

forking编程

[root@localhost ~]# vim myfork.py
import os

print('startting...')
print('Hello World!')
[root@localhost ~]# python3 myfork.py 
startting...
Hello World!
[root@localhost ~]# vim myfork.py
import os

print('startting...')
os.fork() 		# 创建子进程,后续代码将同时在父子进程中执行
print('Hello World!')
[root@localhost ~]# python3 myfork.py 
startting...
Hello World!
Hello World!
[root@localhost ~]# vim myfork.py
import os

print('startting...')
retval = os.fork()
if retval: # 父进程
    print('Hello from 父进程')
else:
    print('Hello from 子进程')
print('Hello from both')
[root@localhost ~]# python3 myfork.py 
startting...
Hello from 父进程
Hello from both
Hello from 子进程
Hello from both

forking编程基本思路

• 需要使用os模块 
• os.fork()函数实现forking功能 
• python中,绝大多数的函数只返回一次,os.fork将返回两次 
• 对fork()的调用,针对父进程返回子进程的PID;对于子进程,返回PID0
• os.fork()针对父子进程都有返回值。父进程的返回值是非0值(子进程的ID号),
子进程返回0
• 父进程负责生成子进程
• 子进程负责做具体的工作
• 子进程工作结束后需要彻底结束
[root@localhost ~]# vim ping.sh
#!/bin/bash

date
for ip in 172.40.63.{1..254}
do
    ping -c2 $ip &>/dev/null && echo "$ip:up" || echo "$ip:down"
done
date
[root@localhost ~]# bash ping.sh
........................
[root@localhost ~]# vim myfork2.py
import os

for i in range(3): # 6
    retval = os.fork()
    if not retval:
        print('Hello World!')
        # exit()  # 进程遇到exit将会完全结束
[root@localhost ~]# python3 myfork2.py 
Hello World!
Hello World!
Hello World!
Hello World!
Hello World!
Hello World!
Hello World!
[root@localhost ~]# vim forkping.py
import os
import subprocess

def ping(host):
    result = subprocess.run(
        'ping -c2 %s &> /dev/null' % host,
        shell=True
    )
    if result.returncode == 0:
        print('%s:up' % host)
    else:
        print('%s:down' % host)

if __name__ == '__main__':
    ips = ('192.168.199.%s' % i for i in range(1, 255))
    for ip in ips:
        retval = os.fork()
        if not retval:
            ping(ip)
            exit()
[root@localhost ~]# python3 forkping.py
.................................

forking基础应用

• 编写一个forking脚本
    1. 在父进程中打印“In parent”然后睡眠102. 在子进程中编写循环,循环5次,输出当系统时间,每次循环结束后睡眠13. 父子进程结束后,分别打印“parent exit”和 “child exit”

扫描存活主机

1. 通过ping测试主机是否可达
2. 如果ping不通,不管什么原因都认为主机不可用
3. 通过fork方式实现并发扫描

解决zombie问题

• 父进程通过os.wait()来得到子进程是否终止的信息 
• 在子进程终止和父进程调用wait()之间的这段时间,子进程被称为zombie(僵尸)进程 
• 如果子进程还没有终止,父进程先退出了,那么子进 程会持续工作。系统自动将子进程的父进程设置为init进程,init将来负责清理僵尸进程
[root@localhost ~]# vim zb.py
import os
import time

print('starting...')
retval = os.fork()
if retval:
    print('in parent, sleeping...')
    time.sleep(30)
    print('parent done')
else:
    print('in child, sleeping...')
    time.sleep(15)
    print('child done.')
    exit()
[root@localhost ~]# python3 zb.py
[root@localhost ~]# watch -n1 ps a # 每隔一秒钟执行ps a
......

解决zombie问题(续1)

• python可以使用waitpid()来处理子进程 
• waitpid()接受两个参数,第一个参数设置为 -1,表示 与wait()函数相同;第二参数如果设置为0表示挂起父进程,直到子程序退出,设置为1表示不挂起父进程
• waitpid()的返回值:如果子进程尚未结束则返回0, 否则返回子进程的PID
[root@localhost ~]# vim zb2.py
import os
import time

retval = os.fork()
if retval:
    print('in parent')
    result = os.waitpid(-1, 0) # 挂起父进程,直到子进程结束才会继续
    print(result)
    time.sleep(10)
    print('parent done')
else:
    print('in child')
    time.sleep(10)
    print('child done')
    exit()
[root@localhost ~]# python3 zb2.py
..................
[root@localhost ~]# watch -n1 ps a
..................

解决zombie问题(续2)

import os, time
def reap():
	result = os.waitpid(-1,	1)						
	print('Reaped child	process	%d'	% result[0])
    
pid = os.fork()
if pid:
    print 'In parent.Sleeping 15s...'
    time.sleep(15)
    reap()
    time.sleep(5)
    print('parent done')
else:
    print 'In child.Sleeping 5s...'
    time.sleep(5)
    print('Child terminating.')
多线程编程
多线程工作原理
多线程的动机
多线程任务的工作特点
什么是进程
什么是线程
多线程编程1
多线程相关模块
传递函数给Thread类
传递可调用类给Thread类
含有线程的服务器

多线程编程

多线程工作原理

多线程的动机

• 在多线程(MT)编程出现之前,电脑程序的运行由 一个执行序列组成,执行序列按顺序在主机的中央处理器(CPU)中运行 
• 无论是任务本身要求顺序执行还是整个程序是由多个 子任务组成,程序都是按这种方式执行的 
• 即使子任务相互独立,互相无关(即,一个子任务的结果不影响其它子任务的结果)时也是这样 
• 如果并行运行这些相互独立的子任务可以大幅度地提升整个任务的效率

多线程任务的工作特点

• 它们本质上就是异步的,需要有多个并发事务 
• 各个事务的运行顺序可以是不确定的,随机的,不可预测的 
• 这样的编程任务可以被分成多个执行流,每个流都有一个要完成的目标 
• 根据应用的不同,这些子任务可能都要计算出一个中间结果,用于合并得到最后的结果

什么是进程

• 计算机程序只不过是磁盘中可执行的、二进制(或其 它类型)的数据 
• 进程(有时被称为重量级进程)是程序的一次执行 
• 每个进程都有自己的地址空间、内存以及其它记录其运行轨迹的辅助数据 
• 操作系统管理在其上运行的所有进程,并为这些进程公平地分配时间
• 程序是计算机硬盘上存储的一些可执行文件,当程序运行后,就会加载到内存,产生进程。所以进程也可说是加载到内存中的一系列指令,一个进程可以包含很多线程。每个进程都有自己独立的运行环境,如内存等;但是同一进程内的多个线程,共享进程的运行环境。

什么是线程

• 线程(有时被称为轻量级进程)跟进程有些相似。不同的是,所有的线程运行在同一个进程中,共享相同的运行环境 
• 一个进程中的各个线程之间共享同一片数据空间,所以线程之间可以比进程之间更方便地共享数据以及相互通讯

多线程编程

多线程编程思路

• 主线程生成工作线程
• 工作线程做具体工作。当工作线程工作完成后,它就自动结束了,也不会产生僵尸进程。

多线程相关模块

• thread和threading模块允许程序员创建和管理线程 
• thread模块提供了基本的线程和锁的支持,而threading提供了更高级别、功能更强的线程管理功能
• 推荐使用更高级别的threading模块

传递函数给Thread类

• 多线程编程有多种方法,传递函数给threading模块的Thread类是介绍的第一种方法 
• Thread对象使用start()方法开始线程的执行,使用join()方法挂起程序,直到线程结束

传递可调用类给Thread类

• 传递可调用类给Thread类是介绍的第二种方法 
• 相对于一个或几个函数来说,由于类对象里可以使用类的强大的功能,可以保存更多的信息,这种方法更为灵活

扫描存活主机

1. 通过ping测试主机是否可达
2. 如果ping不通,不管什么原因都认为主机不可用
3. 通过多线程方式实现并发扫描
[root@localhost ~]# vim mtping.py
import threading
import subprocess

def ping(host):
    result = subprocess.run(
        'ping -c2 %s &> /dev/null' % host,
        shell=True
    )
    if result.returncode == 0:
        print('%s:up' % host)
    else:
        print('%s:down' % host)

if __name__ == '__main__':
    ips = ('192.168.199.%s' % i for i in range(1, 255))
    for ip in ips:
        # ping(ip)
        t = threading.Thread(target=ping, args=(ip,))
        t.start() # 运行target(*args)
[root@localhost ~]# python3 mtping.py
......................
[root@localhost ~]# vim mtping2.py
import threading
import subprocess

class Ping:
    def __init__(self, host):
        self.host = host

    def __call__(self):
        result = subprocess.run(
            'ping -c2 %s &> /dev/null' % self.host,
            shell=True
        )
        if result.returncode == 0:
            print('%s:up' % self.host)
        else:
            print('%s:down' % self.host)

if __name__ == '__main__':
    ips = ('192.168.199.%s' % i for i in range(1, 255))
    for ip in ips:
        t = threading.Thread(target=Ping(ip))
        t.start() # 运行target()
[root@localhost ~]# python3 mtping2.py
.....................
发布了96 篇原创文章 · 获赞 100 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/qq_43395428/article/details/104659063