系统编程,多进程和多线程
分类:微服架构

os.fork(),os.fork

 

ret = os.fork()
if ret == 0:
    child_suite # 子进程代码
else:
    parent_suite # 父进程代码

  Python中的fork() 函数可以获得系统中进程的PID ( Process ID ),返回0则为子进程,否则就是父进程,然后可以据此对运行中的进程进行操作;

  但是强大的 fork() 函数在Windows版的Python中是无法使用的。。。只能在Linux系统中使用,比如 Ubuntu 15.04,Windows中获取父进程ID可以用 getpid()。

  其他还有如下这些os.函数都无法在Windows中使用:

uname(), 获得系统信息(主机名、操作系统版本、补丁级别、系统构架等)
getuid(), 获取/设置现在进程的真正的用户ID
getgid(), 获取/设置现在进程的群组ID
getsid(), 获取回话ID 或者创建和返回新的SID
geteuid(), 获取/设置当前进程的有效用户ID(GID)
getegid(), 获取/设置当前进程的有小组ID(GID)
getpgid(), 获取/设置进程GID进程PID: 对于get,如果pid为0, 便返回现在进程的进程GID
getlogin(), 返回运行现在进程的用户登录
getloadavg(); 返回代表在过去 1,5,15分钟内的系统平均负载值的元祖

 

  这就是为何部署服务器集群大都采用Linux,不只是因为应用部署在Linux在系统中非常稳定、流畅,更重要的是各种工具SDK的功能更完善。

   

 

ret = os.fork() if ret == 0: child_suite # 子进程代码 else : parent_suite # 父进程代码 Python中的fork() 函数可以获得系统中进程的PID ( Pr...

在现实社会,我们经常需要一种场景,就是同时有多个事情需要执行,如在浏览网页的同时需要听音乐。比如说在跳舞的时候要唱歌。
同样的,在程序中我们也可能需要这种场景。如下面我们以同时听音乐和浏览网页为例。

进程的创建-fork

初步解析Python下的多进程编程,解析python编程

要让Python程序实现多进程(multiprocessing),我们先了解操作系统的相关知识。

Unix/Linux操作系统提供了一个fork()系统调用,它非常特殊。普通的函数调用,调用一次,返回一次,但是fork()调用一次,返回两次,因为操作系统自动把当前进程(称为父进程)复制了一份(称为子进程),然后,分别在父进程和子进程内返回。

子进程永远返回0,而父进程返回子进程的ID。这样做的理由是,一个父进程可以fork出很多子进程,所以,父进程要记下每个子进程的ID,而子进程只需要调用getppid()就可以拿到父进程的ID。

Python的os模块封装了常见的系统调用,其中就包括fork,可以在Python程序中轻松创建子进程:

# multiprocessing.py
import os

print 'Process (%s) start...' % os.getpid()
pid = os.fork()
if pid==0:
  print 'I am child process (%s) and my parent is %s.' % (os.getpid(), os.getppid())
else:
  print 'I (%s) just created a child process (%s).' % (os.getpid(), pid)

运行结果如下:

Process (876) start...
I (876) just created a child process (877).
I am child process (877) and my parent is 876.

由于Windows没有fork调用,上面的代码在Windows上无法运行。由于Mac系统是基于BSD(Unix的一种)内核,所以,在Mac下运行是没有问题的,推荐大家用Mac学Python!

有了fork调用,一个进程在接到新任务时就可以复制出一个子进程来处理新任务,常见的Apache服务器就是由父进程监听端口,每当有新的http请求时,就fork出子进程来处理新的http请求。
multiprocessing

如果你打算编写多进程的服务程序,Unix/Linux无疑是正确的选择。由于Windows没有fork调用,难道在Windows上无法用Python编写多进程的程序?

由于Python是跨平台的,自然也应该提供一个跨平台的多进程支持。multiprocessing模块就是跨平台版本的多进程模块。

multiprocessing模块提供了一个Process类来代表一个进程对象,下面的例子演示了启动一个子进程并等待其结束:

from multiprocessing import Process
import os

# 子进程要执行的代码
def run_proc(name):
  print 'Run child process %s (%s)...' % (name, os.getpid())

if __name__=='__main__':
  print 'Parent process %s.' % os.getpid()
  p = Process(target=run_proc, args=('test',))
  print 'Process will start.'
  p.start()
  p.join()
  print 'Process end.'

执行结果如下:

Parent process 928.
Process will start.
Run child process test (929)...
Process end.

创建子进程时,只需要传入一个执行函数和函数的参数,创建一个Process实例,用start()方法启动,这样创建进程比fork()还要简单。

join()方法可以等待子进程结束后再继续往下运行,通常用于进程间的同步。
Pool

如果要启动大量的子进程,可以用进程池的方式批量创建子进程:

from multiprocessing import Pool
import os, time, random

def long_time_task(name):
  print 'Run task %s (%s)...' % (name, os.getpid())
  start = time.time()
  time.sleep(random.random() * 3)
  end = time.time()
  print 'Task %s runs %0.2f seconds.' % (name, (end - start))

if __name__=='__main__':
  print 'Parent process %s.' % os.getpid()
  p = Pool()
  for i in range(5):
    p.apply_async(long_time_task, args=(i,))
  print 'Waiting for all subprocesses done...'
  p.close()
  p.join()
  print 'All subprocesses done.'

执行结果如下:

Parent process 669.
Waiting for all subprocesses done...
Run task 0 (671)...
Run task 1 (672)...
Run task 2 (673)...
Run task 3 (674)...
Task 2 runs 0.14 seconds.
Run task 4 (673)...
Task 1 runs 0.27 seconds.
Task 3 runs 0.86 seconds.
Task 0 runs 1.41 seconds.
Task 4 runs 1.91 seconds.
All subprocesses done.

代码解读:

对Pool对象调用join()方法会等待所有子进程执行完毕,调用join()之前必须先调用close(),调用close()之后就不能继续添加新的Process了。

请注意输出的结果,task 0,1,2,3是立刻执行的,而task 4要等待前面某个task完成后才执行,这是因为Pool的默认大小在我的电脑上是4,因此,最多同时执行4个进程。这是Pool有意设计的限制,并不是操作系统的限制。如果改成:

p = Pool(5)

就可以同时跑5个进程。

由于Pool的默认大小是CPU的核数,如果你不幸拥有8核CPU,你要提交至少9个子进程才能看到上面的等待效果。
进程间通信

Process之间肯定是需要通信的,操作系统提供了很多机制来实现进程间的通信。Python的multiprocessing模块包装了底层的机制,提供了Queue、Pipes等多种方式来交换数据。

我们以Queue为例,在父进程中创建两个子进程,一个往Queue里写数据,一个从Queue里读数据:

from multiprocessing import Process, Queue
import os, time, random

# 写数据进程执行的代码:
def write(q):
  for value in ['A', 'B', 'C']:
    print 'Put %s to queue...' % value
    q.put(value)
    time.sleep(random.random())

# 读数据进程执行的代码:
def read(q):
  while True:
    value = q.get(True)
    print 'Get %s from queue.' % value

if __name__=='__main__':
  # 父进程创建Queue,并传给各个子进程:
  q = Queue()
  pw = Process(target=write, args=(q,))
  pr = Process(target=read, args=(q,))
  # 启动子进程pw,写入:
  pw.start()
  # 启动子进程pr,读取:
  pr.start()
  # 等待pw结束:
  pw.join()
  # pr进程里是死循环,无法等待其结束,只能强行终止:
  pr.terminate()

运行结果如下:

Put A to queue...
Get A from queue.
Put B to queue...
Get B from queue.
Put C to queue...
Get C from queue.

在Unix/Linux下,multiprocessing模块封装了fork()调用,使我们不需要关注fork()的细节。由于Windows没有fork调用,因此,multiprocessing需要“模拟”出fork的效果,父进程所有Python对象都必须通过pickle序列化再传到子进程去,所有,如果multiprocessing在Windows下调用失败了,要先考虑是不是pickle失败了。
小结

在Unix/Linux下,可以使用fork()调用实现多进程。

要实现跨平台的多进程,可以使用multiprocessing模块。

进程间通信是通过Queue、Pipes等实现的。

要让Python程序实现多进程(multiprocessing),我们先了解操作系统的相关知识。 Unix/Linux操作系...

def network():   
   while True:
    print("正在上网~~~")        
    time.sleep(1)def sing():    
  while True:        
    print("正在听歌……")        
    time.sleep(1)
if __name__ == "__main__":    
  network()    
  sing()

进程VS程序

编写完毕的代码,在没有运行的时候,称之为程序

正在运行着的代码,就成为进程

进程,除了包含代码以外,还有需要运行的环境等,所以和程序是有区别的

执行代码,我们发现代码并没有按照我们的想法执行,而是,一直执行上网的代码,这样就无法完成我们想要的效果。
如果你想要同时听音乐和浏览网页两件事,就需要一个新的方式来完成,这个方式就是----多任务。

fork()

Python的os模块封装了常见的系统调用,其中就包括fork,可以在Python程序中轻松创建子进程:

图片 1

说明:

·程序执行到os.fork()时,操作系统会创建一个新的进程(子进程),然后复制父进程的所有信息到子进程中

·然后父进程和子进程都会从fork()函数中得到一个返回值,在子进程中这个值一定是0,而父进程中是子进程的id号

在Unix/Linux操作系统中,提供了一个fork()系统函数,它非常特殊。

普通的函数调用,调用一次,返回一次,但是fork()调用一次,返回两次,因为操作系统自动把当前进程(称为父进程)复制了一份(称为子进程),然后,分别在父进程和子进程内返回。

子进程永远返回0,而父进程返回子进程的ID。

这样做的理由是,一个父进程可以fork出很多子进程,所以,父进程要记下每个子进程的ID,而子进程只需要调用getppid()就可以拿到父进程的ID。

多任务是咋回事呢?

什么叫做多任务呢?简单的说,就是操作系统(OS)可以同时运行多个任务。如你可以一边浏览网页,一边听着歌,同时还可以使用画板画着画,这个就是多任务。其实我们的操作系统就是多任务,只是我们都没有注意罢了。
单核CPU中:
1、时间片轮换
2、优先级别调度
3、多核CPU中:
4、并发
5、并行

getpid()、getppid()

图片 2

图片 3

Fork子进程

编写完的代码,在没有运行的情况下,称之为程序。
正在运行的代码,就成为了进程。
进程,除了包含代码外,还需要运行环境等,所以和程序是存在区别的。
Python在os模块上封装了常用的系统调用,其中就包括了fork,我们可以很轻松地在Python代码中创建进程。

# 注意,下面代码只能在Linux、Mac系统上运行,window下不支持运行!!!
import osimport time# 使用os模块的fork方法可以创建一个新的进程
pid = os.fork()if pid == 0:    
while True:       
  print("son process", pid) # 子进程代码        
  time.sleep(1)
else:    
  while True:        
    print("main process", pid) # 父进程代码        
    time.sleep(1)

Python中的fork() 函数可以获得系统中进程的PID ( Process ID ),返回0则为子进程,否则就是父进程,然后可以据此对运行中的进程进行操作;

但是强大的 fork() 函数在Windows版的Python中是无法使用的。。。只能在Linux系统中使用,比如 Ubuntu 15.04,Windows中获取父进程ID可以用 getpid()。

多进程修改全局变量

图片 4

总结:

·多进程中,每个进程中所有数据(包括全局变量)都各有拥有一份,互不影响

getpid、getppid

我们可以使用os模块的getpid来获取当前进程的进程编号,同样也可以使用os.getppid()方法案来获取当前进程的父进程编号。

import osimport time# 使用os模块的fork方法可以创建一个新的进程
pid = os.fork()
if pid == 0:    
  while True:        
    print("我是子线程,我的编号是%s,我的父进程编号是%s(os.getpid(),os.getppid()))        
    time.sleep(1)
else:    
  while True:        
    print("我是父线程,我的编号是%s,我的子进程编号是%s" %(os.getpid(), pid))        
    time.sleep(1)

使用os模块中的getpid()方法,可以得到当前线程的编号,使用getppid()方法可以得到该线程的父级编号。

多次fork问题

图片 5

图片 6

多进程修改全局变量多进程修改全局变量

import os
import time

# 使用os模块的fork方法可以创建一个新的进程
pid = os.fork()

if pid == 0:
    while True:
        print("我是子线程,我的编号是%s,我的父进程编号是%s" %(os.getpid(),os.getppid()))
        time.sleep(1)
else:
    while True:
        print("我是父线程,我的编号是%s,我的子进程编号是%s" %(os.getpid(), pid))
        time.sleep(1)

使用os模块中的getpid()方法,可以得到当前线程的编号,使用getppid()方法可以得到该线程的父级编号。

multiprocessing

如果你打算编写多进程的服务程序,Unix/Linux无疑是正确的选择。由于Windows没有fork调用,难道在Windows上无法用Python编写多进程的程序?

由于Python是跨平台的,自然也应该提供一个跨平台的多进程支持。multiprocessing模块就是跨平台版本的多进程模块。

multiprocessing模块提供了一个Process类来代表一个进程对象,下面的例子演示了启动一个子进程并等待其结束:

图片 7

说明

·创建子进程时,只需要传入一个执行函数和函数的参数,创建一个Process实例,用start()方法启动,这样创建进程比fork()还要简单。

·join()方法可以等待子进程结束后再继续往下运行,通常用于进程间的同步。

Process语法结构如下:

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

·target:表示这个进程实例所调用对象;

·args:表示调用对象的位置参数元组;

·kwargs:表示调用对象的关键字参数字典;

·name:为当前进程实例的别名;

·group:大多数情况下用不到;

Process类常用方法:

·is_alive():判断进程实例是否还在执行;

·join([timeout]):是否等待进程实例执行结束,或等待多少秒;

·start():启动进程实例(创建子进程);

·run():如果没有给定target参数,对这个对象调用start()方法时,就将执行对象中的run()方法;

·terminate():不管任务是否完成,立即终止;

Process类常用属性:

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

·pid:当前进程实例的PID值;

图片 8

图片 9

图片 10

多进程修改全局变量

import os

# 定义一个全局变量
num = 0

pid = os.fork()

if pid == 0:
    # 子进程
    num += 1
    print("子进程~~~~~~",num)

else:
    #父进程
    num += 1
    print("父进程~~~~~~", num)

输出结果如下

父进程~~~~~~ 1
子进程~~~~~~ 1

由此可见,进程间是无法共享数据的。
注意:多个进程间,每个进程的所有数据(包括全局变量)都是各自拥有一份的,互不影响。

完成最开始的任务

import os
import time

def network():
    for x in range(5):
        print("正在上网~~~")
        time.sleep(1)

def singe():
    for x in range(5):
        print("正在听歌……")
        time.sleep(1)

pid = os.fork()

if pid == 0:
    network()
else:
    singe()

print("程序结束~~~")

多个fork问题

上面的所有程序中,我们使用了fork函数,生成了两个进程(一个主进程、一个子进程),但是如果我们在程序中需要多个进程呢?如两次调用fork函数,会生成三个进程吗?

import os,time

res = os.fork()

if res == 0:
    print("一个子线程")
else:
    print("主线程")

ret = os.fork()
if ret == 0:
    print("第三个线程")
else:
    print("第四个线程")

结果如下

主线程
第四个线程
第三个线程
一个子线程
第三个线程
第四个线程

我们发现,主线程和子线程各执行了一次,但是第三个和第四个都执行了两次,为什么了,看下面的图。

图片 11

1.png

主进程和子进程的执行顺序

其实通过上面的代码,我们已经发现了,主进程和子进程的执行顺序是没有规律的,这个不受程序员的控制,有操作系统的调度算法来控制。

multiprocessing

前面我们使用os.fork()方法实现了多进程,但是这种方案只能在Linux下运行,window环境下是无法运行的,那么有没有可以实现在任何平台都能运行的多进程了,有!Python为大家提供了multiprocessing模块用来实现多进程。

函数实现方式

from multiprocessing import Process
import os

def run():
    print("这个是一个独立的进程",os.getpid(),os.getppid())

if __name__ == "__main__":
    print("代码开始运行……")
    task = Process(target=run)
    task.start() # 启动进程

    print("代码运行结束……",os.getpid())

结果如下

图片 12

2.png

我们发现主进程结束后,子进程才结束,说明我们确实开辟了一个独立的进程。

from multiprocessing import Process
import os

def run(msg):
    for x in range(5):
        print("这个是一个独立的进程",os.getpid(),os.getppid())
        print("传递的参数是:",msg)
    else:
        print("子进程结束了……")
if __name__ == "__main__":
    print("代码开始运行……")
    # target表示独立进程的方法
    #args表示要传递的参数,注意:args的类型是元组,也支持列表list
    task = Process(target=run,args=("这个是参数",)) 
    task.start() # 启动进程

    print("代码运行结束……",os.getpid())

args为调用的子进程的函数的参数,注意类型是一个元组。

from multiprocessing import Process
import os

def run(msg):
    for x in range(5):
        print("这个是一个独立的进程",os.getpid(),os.getppid())
        print("传递的参数是:",msg)
    else:
        print("子进程结束了……")
if __name__ == "__main__":
    print("代码开始运行……")
    # target表示独立进程的方法
    #args表示要传递的参数,注意:args的类型是元组
    task1 = Process(target=run,args=("这个是参数1",))
    task1.start() # 启动进程
    task2 = Process(target=run, args=("这个是参数2",))
    task2.start()  # 启动进程
    task3 = Process(target=run, args=("这个是参数3",))
    task3.start()  # 启动进程

    print("代码运行结束……",os.getpid())

多个进程启动还是一样,执行的顺序同样不可控。
(主进程等待子进程版):
join方法表示只有子进程执行完成后,主进程才能结束。主进程会等待子进程完成后,自身才会执行完成。

from multiprocessing import Process
import os,time

def run(msg):
    print("子进程开始执行了……")
    time.sleep(3)
    print("子进程执行end了……")

if __name__ == "__main__":
    print("代码开始运行……")
    task1 = Process(target=run,args=("这个是参数1",))
    print(task1.is_alive()) # is_alive()方法判断进程是否结束
    task1.join() # 表示这个子进程执行完成,主进程才能继续向下执行
    print(task1.is_alive())
    print("代码运行结束……",os.getpid())

常用方法

from multiprocessing import Process
import os,time

def run(msg):
    print("子进程开始执行了……")
    time.sleep(3)
    print("子进程执行end了……")

if __name__ == "__main__":
    print("代码开始运行……")
    # name表示我们认为的为这个子进程取个名称,
    # 如果不写,默认是Process-n n从1开始
    task1 = Process(target=run,args=("这个是参数1",),name="liujianhong")
    task1.start() # 启动进程
    print(task1.is_alive()) # is_alive()方法判断进程是否结束
    task1.join() # 表示这个子进程执行完成,主进程才能继续向下执行
    print(task1.name) # 得到子进程的名称
    task1.terminate()  # 强制结束进程
    print(task1.is_alive())
    print("代码运行结束……",os.getpid())

多个进程使用不同的方法版

from multiprocessing import Process
import os,time

def run(msg):
    print("子进程1开始执行了……")
    time.sleep(3)
    print("子进程1执行end了……")

def run2(msg):
    print("子进程2开始执行了……")
    time.sleep(3)
    print("子进程2执行end了……")

if __name__ == "__main__":
    print("代码开始运行……")
    task1 = Process(target=run,args=("这个是参数1",))
    task1.start() # 启动进程
    task2 = Process(target=run2, args=("这个是参数2",))
    task2.start()  # 启动进程
    print("代码运行结束……",os.getpid())

类实现方式

在Python中,很多的方案都提供了函数和类两种实现方式,如:装饰器、自定义元类。同样多进程也有两种实现,前面我们已经看了使用函数实现的方式,下面我们使用类来实现以下呗。
进程类的实现非常的简单,只要继承了Process类就ok了,重新该类的run方法,run方法里面的代码,就是我们需要的子进程代码。

from multiprocessing import Process
import time

class MyProcess(Process):

    # 重写run方法即可
    def run(self):
        print("一个子进程开始运行了")
        time.sleep(1)
        print("一个子进程开始运行结束了")

if __name__ == '__main__':
    task = MyProcess()
    task.start()
    print("主进程结束了……")

在进程类的实现中如果想要初始化一些前面我们提到过的参数,如进程名称等,可以使用init借助父类来完成。

from multiprocessing import Process
import time

class MyProcess(Process):

    def __init__(self,name):
        super().__init__(name=name)

    # 重写run方法即可
    def run(self):
        print("一个子进程开始运行了")
        time.sleep(1)
        print("一个子进程开始运行结束了")

if __name__ == '__main__':
    task = MyProcess("刘建宏")
    task.start()
    print(task.name)
    print("主进程结束了……")

进程池Pool

当我们需要的进程数量不多的时候,我们可以使用multiprocessing的Process类来创建进程。但是如果我们需要的进程特别多的时候,手动创建工作量太大了,所以Python也为我们提供了Pool(池)的方式来创建大量进程。

from multiprocessing import Pool
import os,time

def run(msg):
    print("开始一个子线程运行了……")
    time.sleep(1)
    print("开始一个子线程运行结束了……")

if __name__ == "__main__":
    pool = Pool(3)  # 表示初始化一个进程池,最大进程数为5
    for x in range(10):
        pool.apply_async(run, args=("hello pool",))
    print("------start----")
    pool.close() # 关闭池
    pool.join() # 等待所有的子进程完成,必须放在close后面
    print("-------end------")

注意:一般我们使用apply_async这个方法,表示非阻塞的运行,一旦使用了apply方法表示阻塞式执行任务,此时就是单任务执行了(一般不会使用,特殊场景才会使用)

本文由10bet手机官网发布于微服架构,转载请注明出处:系统编程,多进程和多线程

上一篇:fping主机扫描 下一篇:没有了
猜你喜欢
热门排行
精彩图文