精华内容
下载资源
问答
  • 进程

    万次阅读 2021-05-25 14:29:00
    多个进程不能共享全局变量7.启动多个子进程8.多进程文件拷贝9.封装进程对象10.进程间的通信 进程 一.什么是多任务 现代的操作系统(Windows,Mac OS X,Linux,UNIX等)都支持“多任务”。 什么叫做多

    进程

    一.什么是多任务

    现代的操作系统(Windows,Mac OS X,Linux,UNIX等)都支持“多任务”。
    什么叫做多任务:操作系统同时运行多个任务

    • 单核CPU实现多任务原理:操作系统轮流让各个任务交替执行,QQ执行2us,切换到微信,再执行2us,再切换到陌陌,执行2us······。表面上是每个任务反复执行下去,但是CPU调度执行速度太快了,导致我们感觉就像所有任务都在同时执行一样。

    • 多核CPU实现多任务原理:真正的并行执行多任务只能在多核CPU上实现,但是由于任务数量远远多于CPU的核心数量,所以,操作系统也会自动把很多任务轮流调度到每个核心上执行。

    并发:看上去一起执行,任务数多于CPU核心数
    并行:真正一起执行,任务数小于等于CPU核心数

    二、实现多任务的方式:

    1.多进程模式(使用较多)
    2.多线程模式(使用最多)
    3.协程模式 (使用较少)
    4.多进程+多线程模式 (容易出错)

    生活中与很多场景是同时进行的,比如开车的时候手和脚是同时来控制车。还有一些歌手边唱歌边跳舞。
    我们可以理解为我们的大脑控制着我们的身体进行不同的动作。大脑就是cpu,身体的所有支配动作用到的部位为我们的资源。
    试想下如果我们的大脑只能控制一个动作完成后才能控制下一个动作,也就是说我们只能先唱完歌再去跳舞,这是不是很尴尬呢?

    示例1 一般情况下执行唱歌、跳舞

    from time import sleep 
    def sing():
        for i in range(3):
            print('我正在唱歌。。。%d'%i)
            sleep(1)
    
    def dance():
        for i in range(3):
            print('我正在跳舞。。。%d'%i)
            sleep(1)
    if __name__ == '__main__':
        sing() # 先唱歌
        dance() # 再跳舞
    '''
    我正在唱歌。。。0
    我正在唱歌。。。1
    我正在唱歌。。。2
    我正在跳舞。。。0
    我正在跳舞。。。1
    我正在跳舞。。。2'''
    
    

    示例2 单线程执行函数

    from time import sleep
    # 单线程执行
    def saySorry():
        print("亲爱的,圣诞节快乐!")
        sleep(1)
    
    if __name__ == "__main__":
        for i in range(5):
            saySorry()
    """
    # 亲爱的,圣诞节快乐!
    # 亲爱的,圣诞节快乐!
    # 亲爱的,圣诞节快乐!
    # 亲爱的,圣诞节快乐!
    # 亲爱的,圣诞节快乐!
    """
    
    

    示例3 多线程执行函数

    import threading
    from time import sleep
    # 多线程执行
    def saySorry():
        print("亲爱的,圣诞节快乐!")
        sleep(1)
    
    if __name__ == "__main__":
        for i in range(5):
            t = threading.Thread(target=saySorry)
            t.start()  # 启动线程,即让线程开始执行
    """
    # 亲爱的,圣诞节快乐!
    # 亲爱的,圣诞节快乐!
    # 亲爱的,圣诞节快乐!
    # 亲爱的,圣诞节快乐!
    # 亲爱的,圣诞节快乐!
    """
    
    

    示例4多线程执行唱歌、跳舞1

    import threading
    from time import sleep,ctime
    # 多线程执行唱歌跳舞的多任务。
    def sing():
        for i in range(3):
            print("正在唱歌...%d"%i)
            sleep(1)
    
    def dance():
        for i in range(3):
            print("正在跳舞...%d"%i)
            sleep(1)
    
    if __name__ == '__main__':
        print('---开始---:%s'%ctime())
    
        t1 = threading.Thread(target=sing)
        t2 = threading.Thread(target=dance)
    
        t1.start()
        t2.start()
    
        sleep(5)  # 屏蔽此行代码,试试看,程序是否会立马结束?
        print('---结束---:%s'%ctime())
    
    '''
    ---开始---:Tue May 25 11:26:19 2021
    正在唱歌...0
    正在跳舞...0
    正在唱歌...1
    正在跳舞...1
    正在唱歌...2
    正在跳舞...2
    ---结束---:Tue May 25 11:26:24 2021
    '''
    
    

    示例5多线程执行唱歌、跳舞2

    import threading
    from time import sleep,ctime
    # 多线程执行唱歌跳舞的多任务。
    def sing():
        for i in range(3):
            print("正在唱歌...%d"%i)
            sleep(1)
    
    def dance():
        for i in range(3):
            print("正在跳舞...%d"%i)
            sleep(1)
    
    if __name__ == '__main__':
        print('---开始---:%s'%ctime())
    
        t1 = threading.Thread(target=sing)
        t2 = threading.Thread(target=dance)
    
        t1.start()
        t2.start()
    
        # sleep(5) # 屏蔽此行代码,试试看,程序是否会立马结束?
        print('---结束---:%s'%ctime())
    
    '''---开始---:Tue May 25 11:19:25 2021
    正在唱歌...0
    正在跳舞...0---结束---:Tue May 25 11:19:25 2021
    
    正在唱歌...1
    正在跳舞...1
    正在唱歌...2
    正在跳舞...2'''
    
    

    三、什么是进程

    1.什么是进程?

    对于操作系统而言,一个任务就是一个进程,一个应用就是一个进程。
    程序是指令和数据的有序集合,其本身没有任何运行的含义,是一个静态的概念,而进程是程序在处理机上的一次执行过程,它是一个动态的概念。
    进程是系统中程序执行和资源分配的基本单位,每个进程都有自己的数据段、代码段、和堆栈段。
    进程是一个具有一定独立功能的程序,一个实体,每一个进程都有它自己的地址空间。

    2.进程的状态

    进程执行时的间断性,决定了进程可能具有多种状态,事实上,运行中的进程具有以下三种基本状态。
    (1)就绪状态(Ready)
    (2)运行状态(Running)
    (3)阻塞状态(Blocked)

    3.单任务现象

    # 单任务现象
    from time import sleep
    
    def run():
        while True:
            print("haiyan is my wife")
            sleep(2)
    
    if __name__ == "__main__":
        while True:
            print("yangyu is a good man")
            sleep(1)
    
        run()    # 执行不了.因为上面的循环没有结束。
    '''
    执行结果:不停打印
    yangyu is a good man
    yangyu is a good man
    yangyu is a good man
    yangyu is a good man
    yangyu is a good man
    yangyu is a good man
    yangyu is a good man
    yangyu is a good man
    ...
    '''
    
    

    4.启动进程实现多任务

    # 启动进程实现多任务
    """
    multiprocessing 库
    跨平台版本的多进程模块,提供了一个Process类来代表一个进程对象
    """
    from multiprocessing import Process
    import os
    from time import sleep
    
    
    # 子进程需要执行的代码
    def run(str1):
        while True:
            # os.getpid()获取当前进程的id号
            # os.getppid()获取当前父进程的id号
            print("yangyu is a %s man--%s--%s" % (str1, os.getpid(), os.getppid()))
            sleep(1.2)
    
    
    if __name__ == "__main__":
        print("主(父)进程启动-%s" % (os.getpid()))
        # 创建子进程
        # target说明进程执行的任务
        p = Process(target=run, args=("nice",))
        # 启动进程
        p.start()
    
        while True:
            print("yangyu is a good man")
            sleep(1)
    '''
    主(父)进程启动-2372
    yangyu is a good man
    yangyu is a good man
    yangyu is a nice man--5664--2372
    yangyu is a good man
    yangyu is a good man
    yangyu is a nice man--5664--2372
    yangyu is a good man
    yangyu is a nice man--5664--2372
    yangyu is a good man
    yangyu is a nice man--5664--2372
    yangyu is a good man
    yangyu is a nice man--5664--2372
    yangyu is a good man
    yangyu is a nice man--5664--2372
    yangyu is a good man
    yangyu is a good man
    yangyu is a nice man--5664--2372'''
    
    

    5.父子进程的先后顺序

    # 父子进程的先后顺序
    from multiprocessing import Process
    from time import sleep
    
    def run(str1,str2):
        print("子进程启动+%s"%str1)
        sleep(3)
        print("子进程结束+%s"%str2)
    
    if __name__ == "__main__":
        print("父进程启动")
    
        p = Process(target=run, args=("start","end"))
        p.start()
    
        # 父进程的结束不能影响子进程,让父进程等待子进程的结束再执行父进程
        p.join()
        print("父进程结束")
    '''
    父进程启动
    子进程启动+start
    子进程结束+end
    父进程结束
    '''
    
    

    6.多个进程不能共享全局变量

    # 多个进程不能共享全局变量
    # 在子进程中修改全局变量对父进程中的全局变量没有影响
    # 在创建子进程时对全局变量做了一个备份,父进程中的子进程中的num是完全不同的两个变量
    from multiprocessing import Process
    num = 100
    def run():
        print("子进程开始")
        global num  # num = 100
        num += 1
        print(num)
        print("子进程结束")
    
    if __name__ == "__main__":
        print("父进程开始")
        p = Process(target=run)
        p.start()
        p.join()
        print("父进程结束--%d"%num)
    '''
    父进程开始
    子进程开始
    101
    子进程结束
    父进程结束--100
    '''
    
    

    7.启动多个子进程

    # 启动多个子进程
    from multiprocessing import Pool
    import os,time
    def run(name):
        print("子进程%d启动--%s" % (name, os.getpid()))
        start = time.time()
        time.sleep(3)
        end = time.time()
        print("子进程%d结束--%s--耗时%.2f" % (name, os.getpid(), end-start))
    
    if __name__ == "__main__":
        t1 = time.time()
        print("父进程启动")
        # 创建多个进程
        # 进程池
        # 表示可以同时执行的进程数量
        # Pool默认大小是cpu核心线程数,我的笔记本电脑是2核4线程(这里的线程就同时执行的进程),Pool()默认为4,默认同时执行4进程,总耗时为18.2s;改为2,总耗时为32.5s;改为8,总耗时14.1s;改为20,总耗时20.35s。所有一般就用核心数*线程数(2*4=8),执行速度最快。
        pp = Pool(8)
        for i in range(20):
            # 创建进程,放入进程池同意管理
            pp.apply_async(run, args=(i,))
        # 在调用join之前必须先调用close,调用close之后就不能再继续添加新的进程了
        pp.close()
        # 进程池对象调用join,会等待进程池中所有的子进程结束完毕再去执行父进程
        pp.join()
        print("父进程结束,总耗时为%s"%(time.time()-t1))
    '''
    父进程启动
    子进程0启动--9048
    子进程1启动--3648
    子进程2启动--7180
    子进程3启动--7888
    子进程4启动--8228
    子进程5启动--8664
    子进程6启动--8688
    子进程7启动--9432
    子进程0结束--9048--耗时3.01
    子进程8启动--9048
    子进程1结束--3648--耗时3.01
    子进程9启动--3648
    子进程2结束--7180--耗时3.01
    子进程10启动--7180
    子进程3结束--7888--耗时3.01
    子进程11启动--7888
    子进程4结束--8228--耗时3.01
    子进程12启动--8228
    子进程5结束--8664--耗时3.01
    子进程13启动--8664
    子进程6结束--8688--耗时3.01
    子进程14启动--8688
    子进程7结束--9432--耗时3.01
    子进程15启动--9432
    子进程8结束--9048--耗时3.07
    子进程16启动--9048
    子进程9结束--3648--耗时3.01
    子进程17启动--3648
    子进程10结束--7180--耗时3.01
    子进程18启动--7180
    子进程11结束--7888--耗时3.01
    子进程19启动--7888
    子进程12结束--8228--耗时3.01
    子进程13结束--8664--耗时3.01
    子进程14结束--8688--耗时3.01
    子进程15结束--9432--耗时3.01
    子进程16结束--9048--耗时3.01
    子进程17结束--3648--耗时3.01
    子进程18结束--7180--耗时3.04
    子进程19结束--7888--耗时3.01
    父进程结束,总耗时为14.196025133132935
    '''
    
    

    8.多进程文件拷贝

    1.普通文件拷贝

    # 实现文件的拷贝
    import os, time
    def copyFile(rPath, wPath):
        fr = open(rPath, "rb")
        fw = open(wPath, "wb")
        context = fr.read()
        fw.write(context)
        fr.close()
        fw.close()
    
    path = r"F:\PycharmProjects\Project\进程\file1\1905热门电影图片"
    toPath = r"F:\PycharmProjects\Project\进程\file2"
    
    # 读取path下的所有文件
    fileList = os.listdir(path)
    
    # 启动for循环去处理每个文件
    start = time.time()
    for fileName in fileList:
        copyFile(os.path.join(path,fileName), os.path.join(toPath, fileName))
    end = time.time()
    print("总耗时:%0.2f" % (end-start))  # 总耗时:14.68
    

    2.多进程文件拷贝

    import os, time
    from multiprocessing import Pool
    # 实现文件的拷贝
    def copyFile(rPath, wPath):
        fr = open(rPath, "rb")
        fw = open(wPath, "wb")
        context = fr.read()
        fw.write(context)
        fr.close()
        fw.close()
    
    path = r"F:\PycharmProjects\Project\进程\file1\1905热门电影图片"
    toPath = r"F:\PycharmProjects\Project\进程\file2"
    
    if __name__ == "__main__":
        # 读取path下的所有文件
        fileList = os.listdir(path)
        start = time.time()
        pp = Pool(4)
        for fileName in fileList:
            pp.apply_async(copyFile, args=(os.path.join(path, fileName), os.path.join(toPath, fileName)))
        pp.close()
        pp.join()
        end = time.time()
        print("总耗时:%0.2f" % (end - start))  # 总耗时:11.40
    

    9.封装进程对象

    1.创建yangyuProcess.py文件

    from multiprocessing import Process
    import os, time
    
    class YangyuProcess(Process):
        def __init__(self, name):
            Process.__init__(self)
            self.name = name
    
        def run(self):
            print("子进程(%s-%s)启动" % (self.name, os.getpid()))
    
            # 子进程的功能
            time.sleep(3)
    
            print("子进程(%s-%s)结束" % (self.name, os.getpid()))
    

    2.from yangyuProcess import YangyuProcess

    from yangyuProcess import YangyuProcess
    
    if __name__ == "__main__":
        print("父进程启动")
        # 创建子进程
        p = YangyuProcess("test")
        # 自动调用p进程对象的run方法
        p.start()
        p.join()
        print("父进程结束")
    '''
    打印结果:
    父进程启动
    子进程(test-3476)启动
    子进程(test-3476)结束
    父进程结束
    '''
    """
    这样写的好处:子进程的方法不用和父进程的方法写在一起,让主程序结构更清晰。
    """
    

    10.进程间的通信

    1.示例1

    from multiprocessing import Process, Queue
    import os,time
    
    def write(q):
        print("启动写子进程%s"%(os.getpid()))
        for chr in ["A","B","C","D"]:
            q.put(chr)
            time.sleep(1)
        print("结束写子进程%s"%(os.getpid()))
    
    def read(q):
        print("启动读子进程%s" % (os.getpid()))
        while True:
            value = q.get(True)
            print("value=" + value)
        print("结束读子进程%s" % (os.getpid()))
    
    
    if __name__ == "__main__":
        # 父进程创建队列,并传给子进程
        q = Queue()
        pw = Process(target=write, args=(q,))
        pr = Process(target=read, args=(q,))
    
        pw.start()
        pr.start()
    
        pw.join()
        pr.terminate()  # pr进程里是个死循环,无法等待其结束,只能强制结束
    
        print("父进程结束")
    
    '''
    启动写子进程7752
    启动读子进程7264
    value=A
    value=B
    value=C
    value=D
    结束写子进程7752
    父进程结束'''
    
    

    2.示例2

    from multiprocessing import Process, Queue
    import os,time
    
    def write(q):
        print("启动写子进程%s"%(os.getpid()))
        for chr in ["A","B","C","D"]:
            q.put(chr)
            print(chr)
        print("结束写子进程%s"%(os.getpid()))
    
    def read(q):
        print("启动读子进程%s" % (os.getpid()))
        while not q.empty():
            value = q.get(True)
            print("value=" + value)
            time.sleep(1)
        print("结束读子进程%s" % (os.getpid()))
    
    
    if __name__ == "__main__":
        print("父进程开始")
        # 父进程创建队列,并传给子进程
        q = Queue(4)
        pw = Process(target=write, args=(q,))
        pr = Process(target=read, args=(q,))
    
        pw.start()
        pr.start()
        pw.join()
        pr.join()
        print("父进程结束")
    
    '''
    父进程开始
    启动写子进程8228
    A
    B
    C
    D
    结束写子进程8228
    启动读子进程7472
    value=A
    value=B
    value=C
    value=D
    结束读子进程7472
    父进程结束
    '''
    
    展开全文
  • python多进程处理

    千次阅读 2014-11-18 20:22:35
    最近处理fMRI数据时由于采用了的算法需要随机成千上万次,因此所需的计算量相当大,

    最近处理fMRI数据时由于采用了的算法需要随机成千上万次,因此所需的计算量相当大, 单线程处理的话一般要一天,python原生是不支持多线程的,因此考虑采用python的多进程。其实编写python多进程的程序还是比较简单。下面实在自己的电脑上开启了4进程。


    任务管理器里面CPU显示已经占用了100%。



    准备开始coding...

    首先导入相应的多进程的包:

    import multiprocessing

    下面就可以开始编写多就进程的程序了:

    pool = multiprocessing.Pool(processes=multiprocessing.cpu_count())
    pool_outputs = pool.map(func,  var_list)
    pool.close()
    pool.join()


    第一行代码表示获取CPU核心的数量,第二行的map函数的第一个参数是多进程需要调用的函数, var_list是参数,是一个list类型的。pool_outputs 是多进程处理后返回的处理结果。



    展开全文
  • 阅读目录 一:nodejs进程进化及多进程架构原理 ...NodeJS是基于chrome浏览器的V8引擎构建的,它是单线程单进程模式,nodeJS的单线程指js的引擎只有一实列。且是在主线程执行的,这样的优点是:可以减少线程间切换...

    阅读目录

    回到顶部

    一:nodejs进程进化及多进程架构原理

    NodeJS是基于chrome浏览器的V8引擎构建的,它是单线程单进程模式,nodeJS的单线程指js的引擎只有一个实列。且是在主线程执行的,这样的
    优点是:可以减少线程间切换的开销。并且不用考虑锁和线程池的问题。

    那么nodejs是单线程吗?如果严格的来讲,node存在着多种线程。比如包括:js引擎执行的线程、定时器线程、异步http线程等等这样的。

    nodejs是在主线程执行的,其他的异步IO和事件驱动相关的线程是通过libuv来实现内部的线程池和线程调度的。libuv存在着一个Event Loop,通过 Event Loop(事件循环)来切换实现类似多线程的效果。Event Loop 是维持一个执行栈和一个事件队列,在执行栈中,如果有异步IO及定时器等函数的话,就把这些异步回调函数放入到事件队列中。等执行栈执行完成后,会从事件队列中,按照一定的顺序执行事件队列中的异步回调函数。
    nodeJS中的单线程是指js引擎只在唯一的主线程上运行的。其他的异步操作是有独立的线程去执行。通过libuv的Event Loop实现了类似多线程的上下文切换以及线程池的调度。线程是最小的进程,因此node也是单进程的。

    理解服务器进程进化

    1. 同步单进程服务器

    该服务器是最早出现的,执行模型是同步的。它的服务模式是一次只能处理一个请求。其他的请求需要按照顺序依次等待处理执行。也就是说如果当前的请求正在处理的话,那么其他的请求都处于阻塞等待的状态。因此这样的服务器处理速度是不好的。

    2. 同步多进程服务器

    为了解决上面同步单进程服务器无法处理并发的问题,我们就出来一个同步多进程服务器,它的功能是一个请求需要一个进程来服务,也就是说如果有100个请求就需要100个进程来进行服务。那么这样就会有很大进程的开销问题了。并且相同的状态在内存中会有多种,这样就会造成资源浪费。

    3. 同步多进程多线程服务器

    为了解决上面多进程中资源浪费的问题,我们就引入了多进程多线程服务器模式,从我们之前一个进程处理一个请求,现在我们改成为一个线程来处理一个请求,线程相对于进程来说开销会少很多,并且线程之间还可以共享数据。并且我们还可以使用线程池来减少创建和销毁线程的开销。
    但是多线程也有缺点,比如多个请求需要使用多个线程来服务,但是每个线程需要一定的内存来存放自己的堆和栈的。这样就会导致占用太多的内存。第二就是:CPU核心只能处理一件事情,系统是通过将CPU切分为时间片的方法来让线程可以均匀地使用CPU的资源的。在系统切换线程的过程中也会进行线程上下文切换,当线程数量过多时进行上下文切换会非常耗费时间的。因此在很大的并发量下,多线程还是无法做到很好的伸缩性。Apache服务器就是这样架构的。

    4. 单进程单线程基于事件驱动的服务器

    为了解决上面的问题,我们出现了单进程单线程基于事件驱动的模式出现了,使用单线程的优点是:避免内存开销和上下文切换的开销。
    所有的请求都在单线程上执行的,其他的异步IO和事件驱动相关的线程是通过libuv中的事件循环来实现内部的线程池和线程调度的。可伸缩性比之前的都好,但是影响事件驱动服务模型性能的只有CPU的计算能力,但是只能使用单核的CPU来处理事件驱动,但是我们的计算机目前都是多核的,我们要如何使用多核CPU呢?如果我们使用多核CPU的话,那么CPU的计算能力就会得到一个很大的提升。

    5. NodeJS的实现多进程架构

    如上第四点,面对单线程单进程对多核使用率不好的问题,因此我们使用多进程,每个进程使用一个cpu,因此我们就可以实现多核cpu的利用。
    Node提供了child_process模块和cluster模块来实现多进程以及进程的管理。也就是我们常说的 Master-Worker模式。也就是说进程分为Master(主)进程 和 worker(工作)进程。master进程负责调度或管理worker进程,那么worker进程负责具体的业务处理。在服务器层面来讲,worker可以是一个服务进程,负责出来自于客户端的请求,多个worker就相当于多个服务器,因此就构成了一个服务器群。master进程则负责创建worker,接收客户端的请求,然后分配到各个服务器上去处理,并且监控worker进程的运行状态及进行管理操作。

    如下图所示:

    回到顶部

    二:node中child_process模块实现多进程

    nodejs 是单进程的,因此无法使用多核cpu,node提供了child_process模块来实现子进程。从而会实现一个广义上的多进程模式,通过child_process模块,可以实现一个主进程,多个子进程模式,主进程叫做master进程,子进程叫做worker(工作)进程,在子进程中不仅可以调用其他node程序,我们还可以调用非node程序及shell命令等。执行完子进程后,我们可以以流或回调形式返回给主进程。

    child_process提供了4个方法,用于创建子进程,这四个方法分别为 spawn, execFile, exec 和 fork. 所有的方法都是异步的。

    该如上4个方法的区别是什么?

    spawn: 子进程中执行的是非node程序,提供一组参数后,执行的结果以流的形式返回。
    execFile: 子进程中执行的是非node程序, 提供一组参数后,执行的结果以回调的形式返回。
    exec: 子进程执行的是非node程序,提供一串shell命令,执行结果后以回调的形式返回,它与 execFile不同的是,exec可以直接执行一串
    shell命令。

    fork: 子进程执行的是node程序,提供一组参数后,执行的结果以流的形式返回,它与spawn不同的是,fork生成的子进程只能执行node应用。

    2.1 execFile 和 exec

    该两个方法的相同点和不同点如下:

    相同点:执行的都是非node应用,且执行的结果以回调函数的形式返回。
    不同点:execFile执行的是一个应用,exec执行的是一段shell命令。

    比如来说:echo是Unix系统的一个自带命令,我们可以直接在命令行中执行如下命令:

    echo hello world

    如下所示:

    如上可以看到,我们在命令行中会打印 hello world. 因此这个我们可以使用 exec 来实现。

    1)通过exec来实现:

    exec执行shell命令代码如下:

    const cp = require('child_process');
    console.log(cp);
    cp.exec('echo hello world', function(err, res) {
      console.log(res);
    });

    执行如下图所示:

    如上我们可以看到,我们的 child_process模块有如下属性:

    复制代码

    { ChildProcess: [Function: ChildProcess],
      fork: [Function: fork],
      _forkChild: [Function: _forkChild],
      exec: [Function: exec],
      execFile: [Function: execFile],
      spawn: [Function: spawn],
      spawnSync: [Function: spawnSync],
      execFileSync: [Function: execFileSync],
      execSync: [Function: execSync] }

    复制代码

    执行如上exec命令后,结果输出为 hello world.

    2) 通过execFile实现

    const cp = require('child_process');
    cp.execFile('echo', ['hello', 'world'], function(err, res) {
      console.log(res);
    });

    如上结果也是为 "hello world".

    2.2 spawn

    spawn是用于执行非node应用的,并且是不能直接执行shell。spawn执行的结果是以流的形式输出的,通过流的方式可以节约内存的。

    2.3 fork

    在node中提供了fork方法,通过使用fork方法在单独的进程中执行node程序,通过使用fork新建worker进程,上下文都复制主进程。并且通过父子之间的通信,子进程接收父进程的信息,并执行子进程后结果信息返回给父进程。降低了大数据运行的压力。

    现在我们来理解下使用fork()方法来创建子进程,fork()方法只需要指定要执行的javascript文件模块,即可创建Node的子进程。下面我们是简单的hello world的demo,master进程根据cpu的数量来创建出相应数量的worker进程,worker进程利用进程ID来标记。

    |------ 项目
    |  |--- master.js
    |  |--- worker.js
    |  |--- package.json
    |  |--- node_modules

    如上是我们的简单项目结构,其中 worker.js 代码如下:

    console.log('Worker-' + process.pid + ': Hello world.');

    master.js 代码如下:

    复制代码

    const childProcess = require('child_process');
    const cpuNum = require('os').cpus().length;
    
    for (let i = 0; i < cpuNum; ++i) {
      childProcess.fork('./worker.js');
    }
    
    console.log('Master: xxxx');

    复制代码

    然后我们进入项目中的根目录,执行 node master.js 命令即可看到打印信息如下:

    如上图可以看到,我们的master创建了4个worker进程后输出 hello world信息。如上就是根据cpu的数量创建了4个工作进程。

    回到顶部

    三:父子进程间如何通信?

    如上创建了4个worker进程后,现在我们需要考虑的是如何实现 master进程与worker进程通信的问题。

    在NodeJS中父子进程之间通信可以通过 on('message') 和 send()方法来实现通信,on('message') 是监听message事件的。
    当该进程收到其他进程发送的消息时候,便会触发message事件。send()方法则是用于向其他进程发送消息的。

    具体如何做呢?

    master进程中可以调用 child_process的fork()方法后会得到一个子进程的实列,通过该实列我们可以监听到来自子进程的消息或向子进程发送消息。而worker进程则通过process对象接口来监听父进程的消息或向父进程发送消息。现在我们把master.js 代码改成如下:

    复制代码

    const childProcess = require('child_process');
    const worker = childProcess.fork('./worker.js');
    
    // 主进程向子进程发送消息
    worker.send('Hello World');
    
    // 监听子进程发送过来的消息
    worker.on('message', (msg) => {
      console.log('Received message from worker:' + msg);
    });

    复制代码

    worker.js 代码如下:

    复制代码

    // 接收主进程发来的消息
    process.on('message', (msg) => {
      console.log('Received message from master:' + msg);
      // 子进程向主进程发送消息
      process.send('Hi master.');
    });

    复制代码

    我们继续在命令中执行 node master.js 命令后,看到如下信息被打印了 

    3.2 Master实现对Worker的请求进行分发

    如上只是简单的父进程和子进程进行通信的demo实列,现在我们继续来看一个更复杂一点的demo。我们知道master进程最主要是创建子进程,及对子进程进行管理和分配,而子进程最主要做的事情是处理具体的请求及业务。

    进程通信除了使用到上面的send()方法,发送一些普通对象以外,我们还可以发送句柄,什么是句柄呢,句柄是一种引用,可以用来标识资源。
    比如通过句柄可以标识一个socket对象等。我们可以利用该句柄实现请求的分发。

    现在我们通过master进程来创建一个TCP服务器来监听一些特定的端口,master进程会收到客户端的请求,我们会得到一个socket对象,通过这个socket对象就可以和客户端进行通信,从而我们可以处理客户端的请求。

    比如如下demo实列,master创建TCP服务器并且监听8989端口,收到该请求后会将请求分发给worker处理,worker收到master发来的socket以后,通过socket对客户端的响应。

    复制代码

    |------ 项目
    |  |--- master.js
    |  |--- worker.js
    |  |--- tcp_client.js
    |  |--- package.json
    |  |--- node_modules

    复制代码

    master.js 代码如下:

    复制代码

    const childProcess = require('child_process');
    const net = require('net');
    
    // 获取cpu的数量
    const cpuNum = require('os').cpus().length;
    
    let workers = [];
    let cur = 0;
    
    for (let i = 0; i < cpuNum; ++i) {
      workers.push(childProcess.fork('./worker.js'));
      console.log('worker process-' + workers[i].pid);
    }
    
    // 创建TCP服务器
    const tcpServer = net.createServer();
    
    /*
     服务器收到请求后分发给工作进程去处理
    */
    tcpServer.on('connection', (socket) => {
      workers[cur].send('socket', socket);
      cur = Number.parseInt((cur + 1) % cpuNum);
    });
    
    tcpServer.listen(8989, () => {
      console.log('Tcp Server: 127.0.0.8989');
    });

    复制代码

    worker.js 代码如下:

    复制代码

    // 接收主进程发来的消息
    process.on('message', (msg, socket) => {
      if (msg === 'socket' && socket) {
        // 利用setTimeout 模拟异步请求
        setTimeout(() => {
          socket.end('Request handled by worker-' + process.pid);
        },100);
      }
    });

    复制代码

    tcp.client.js 代码如下:

    复制代码

    const net = require('net');
    const maxConnectCount = 10;
    
    for (let i = 0; i < maxConnectCount; ++i) {
      net.createConnection({
        port: 8989,
        host: '127.0.0.1'
      }).on('data', (d) => {
        console.log(d.toString());
      })
    }

    复制代码

    如上代码,tcp_client.js 负责创建10个本地请求,master.js 首先根据cpu的数量,创建多个worker进程,然后创建一个tcp服务器,使用connection来监听net中 createConnection 方法创建事件,当有事件来的时候,就使用worker子进程依次进行分发事件,最后我们通过worker.js 来使用 process中message事件对事件进行监听。如果收到消息的话,就打印消息出来,比如如下代码:

    复制代码

    // 接收主进程发来的消息
    process.on('message', (msg, socket) => {
      if (msg === 'socket' && socket) {
        // 利用setTimeout 模拟异步请求
        setTimeout(() => {
          socket.end('Request handled by worker-' + process.pid);
        },100);
      }
    });

    复制代码

    为了查看效果,我们可以在项目的根目录下 运行 命令 node master.js 启动服务器,然后我们打开另一个命令行,执行 node tcp_client.js 启动客户端,然后我们会看到我们的10个请求被分发到不同的服务器上进行处理,如下所示:

    3.3 Worker监听同一个端口

    我们之前已经实现了句柄可以发送普通对象及socket对象外,我们还可以通过句柄的方式发送一个server对象。我们在master进程中创建一个TCP服务器,将服务器对象直接发送给worker进程,让worker进程去监听端口并处理请求。因此master进程和worker进程就会监听了相同的端口了。当我们的客户端发送请求时候,我们的master进程和worker进程都可以监听到,我们知道我们的master进程它是不会处理具体的业务的。
    因此需要使用worker进程去处理具体的事情了。因此请求都会被worker进程处理了。

    那么在这种模式下,主进程和worker进程都可以监听到相同的端口,当网络请求到来的时候,会进行抢占式调度,只有一个worker进程会抢到链接然后进行服务,由于是抢占式调度,可以理解为谁先来谁先处理的模式,因此就不能保证每个worker进程都能负载均衡的问题。下面是一个demo如下:

    master.js 代码如下:

    复制代码

    const childProcess = require('child_process');
    const net = require('net');
    
    // 获取cpu的数量
    const cpuNum = require('os').cpus().length;
    
    let workers = [];
    let cur = 0;
    
    for (let i = 0; i < cpuNum; ++i) {
      workers.push(childProcess.fork('./worker.js'));
      console.log('worker process-' + workers[i].pid);
    }
    
    // 创建TCP服务器
    const tcpServer = net.createServer();
    
    tcpServer.listen(8989, () => {
      console.log('Tcp Server: 127.0.0.8989');
      // 监听端口后将服务器句柄发送给worker进程
      for (let i = 0; i < cpuNum; ++i) {
        workers[i].send('tcpServer', tcpServer);
      }
      // 关闭master线程的端口监听
      tcpServer.close();
    });

    复制代码

    worker.js 代码如下:

    复制代码

    // 接收主进程发来的消息
    process.on('message', (msg, tcpServer) => {
      if (msg === 'tcpServer' && tcpServer) {
        tcpServer.on('connection', (socket) => {
          setTimeout(() => {
            socket.end('Request handled by worker-' + process.pid);
          }, 100);
        })
      }
    });

    复制代码

    tcp_client.js 代码如下:

    复制代码

    const net = require('net');
    const maxConnectCount = 10;
    
    for (let i = 0; i < maxConnectCount; ++i) {
      net.createConnection({
        port: 8989,
        host: '127.0.0.1'
      }).on('data', (d) => {
        console.log(d.toString());
      })
    }

    复制代码

    如上代码,我们运行 node master.js 代码后,运行结果如下所示:

    然后我们进行 运行 node tcp_client.js 命令后,运行结果如下所示:

    如上我们可以看到 进程id为 37660 调度的比较多。

    3.4 实现进程重启

    worker进程可能会因为其他的原因导致异常而退出,为了提高集群的稳定性,我们的master进程需要监听每个worker进程的存活状态,当我们的任何一个worker进程退出之后,master进程能监听到并且能够重启新的子进程。在我们的Node中,子进程退出时候,我们可以在父进程中使用exit事件就能监听到。如果触发了该事件,就可以断定为子进程已经退出了,因此我们就可以在该事件内部做出对应的处理,比如说重启子进程等操作。

    下面是我们上面监听同一个端口模式下的代码demo,但是我们增加了进程重启的功能。进程重启时,我们的master进程需要重新传递tcpServer对象给新的worker进程。但是master进程是不能被关闭的。否则的话,句柄将为空,无法正常传递。

    master.js 代码如下:

    复制代码

    const childProcess = require('child_process');
    const net = require('net');
    
    // 获取cpu的数量
    const cpuNum = require('os').cpus().length;
    
    let workers = [];
    let cur = 0;
    
    for (let i = 0; i < cpuNum; ++i) {
      workers.push(childProcess.fork('./worker.js'));
      console.log('worker process-' + workers[i].pid);
    }
    
    // 创建TCP服务器
    const tcpServer = net.createServer();
    
    /*
     服务器收到请求后分发给工作进程去处理
    */
    tcpServer.on('connection', (socket) => {
      workers[cur].send('socket', socket);
      cur = Number.parseInt((cur + 1) % cpuNum);
    });
    
    tcpServer.listen(8989, () => {
      console.log('Tcp Server: 127.0.0.8989');
      // 监听端口后将服务器句柄发送给worker进程
      for (let i = 0; i < cpuNum; ++i) {
        workers[i].send('tcpServer', tcpServer);
        // 监听工作进程退出事件
        workers[i].on('exit', ((i) => {
          return () => {
            console.log('worker-' + workers[i].pid + ' exited');
            workers[i] = childProcess.fork('./worker.js');
            console.log('Create worker-' + workers[i].pid);
            workers[i].send('tcpServer', tcpServer);
          }
        })(i));
      }
      // 不能关闭master线程的,否则的话,句柄将为空,无法正常传递。
      // tcpServer.close();
    });

    复制代码

    worker.js 代码如下:

    复制代码

    // 接收主进程发来的消息
    process.on('message', (msg, tcpServer) => {
      if (msg === 'tcpServer' && tcpServer) {
        tcpServer.on('connection', (socket) => {
          setTimeout(() => {
            socket.end('Request handled by worker-' + process.pid);
          }, 100);
        })
      }
    });

    复制代码

    tcp_client.js 代码如下:

    复制代码

    const net = require('net');
    const maxConnectCount = 10;
    
    for (let i = 0; i < maxConnectCount; ++i) {
      net.createConnection({
        port: 8989,
        host: '127.0.0.1'
      }).on('data', (d) => {
        console.log(d.toString());
      })
    }

    复制代码

    当我们在命令中 运行 node master.js  和 node tcp_client.js 执行后,如下图所示:

    然后我们进入我们的电脑后台(我这边是mac电脑),进入活动监视器页面,结束某一个进程,如下图所示:

    结束完成后,我们再来看下我们的 node master.js 命令可以看到,先打印 某某工作进程被退出了,然后某某工作进程被创建了,如下图所示

    然后我们再到我们的 活动监视器可以看到新的 进程号被加进来了,如下图所示:

    回到顶部

    四:理解cluster集群

    如上我们了解了使用 child_process实现node集群操作,现在我们来学习使用cluster模块实现多进程服务充分利用我们的cpu资源以外,还能够帮我们更好地进行进程管理。我们使用cluster模块来实现我们上面同样的功能,代码如下:

    master.js 代码如下:

    复制代码

    const cluster = require('cluster');
    if (cluster.isMaster) {
      const cpuNum = require('os').cpus().length;
      for (let i = 0; i < cpuNum; ++i) {
        cluster.fork();
      }
    
      // 创建进程完成后输出信息
      cluster.on('online', (worker) => {
        console.log('Create worker-' + worker.process.pid);
      });
    
      // 监听子进程退出后重启事件
      cluster.on('exit', (worker, code, signal) => {
        console.log('[Master] worker ' + worker.process.pid + ' died with code:' + code + ', and' + signal);
        cluster.fork(); // 重启子进程
      });
    } else {
      const net = require('net');
      net.createServer().on('connection', (socket) => {
        setTimeout(() => {
          socket.end('Request handled by worker-' + process.pid);
        }, 10)
      }).listen(8989)
    }

    复制代码

    如上代码,我们可以使用 cluster.isMaster 来判断是主进程还是子进程,如果是主进程的话,我们使用cluster创建了和cpu数量相同的worker进程,并且通过监听 cluster中的online事件来判断worker是否创建成功。并且使用了 cluster监听了 exit事件,当worker进程退出后,会触发master进程中cluster的online事件来判断worker是否创建成功。如下图我们在命令行中运行命令:

    如下所示:

    我们现在同样的道理,我们去 活动监视器去吧 47575这个端口号结束掉。在看看我们的命令行如下所示:

    从上图我们也可以看到 47575 进程结束掉,并且47898进程重启了。如上代码使用 cluster模块实现了child_process集群的操作。

    有关更多的cluster中的API可以看这篇文章(http://wiki.jikexueyuan.com/project/nodejs/cluster.html)

    我们在下一篇文章会深入学习使用cluster的应用场景demo。 基本原理先到这里。

    展开全文
  • c++多进程编程

    千次阅读 2020-04-16 10:12:52
    c++多进程编程 介绍 进程进程是一正在执行的程序...线程:线程是任务处理的一种特殊形式,任务处理允许让电脑同时运行两或两以上的程序。一般情况下,两种类型的任务处理:基于进程和基于线程。...

    c++多进程编程

    介绍

    • 进程:进程是一个正在执行的程序,是向CPU申请资源的,进程之间数据相互独立,一个进程至少有一个线程。
    • 线程:线程是进程中的单一的顺序控制流程也可以叫做最小控制单元,线程是进程中执行单元,开启一个线程比开启一个进程更加节省资源。
    • 多线程:多线程是多任务处理的一种特殊形式,多任务处理允许让电脑同时运行两个或两个以上的程序。一般情况下,两种类型的多任务处理:基于进程和基于线程。
      多线程程序包含可以同时运行的两个或多个部分。这样的程序中的每个部分称为一个线程,每个线程定义了一个单独的执行路径。
    • 基于进程的多任务处理是程序的并发执行。
    • 基于线程的多任务处理是同一程序的片段的并发执行。

    线程

    线程的优点:

    1、创建一个新线程的代价要比创建一个新进程小得多
    2、与进程之间的切换相比,线程之间的切换需要操作系统做的工作要少很多
    3、线程占用的资源要比进程少很多
    4、能充分利用多处理器的可并行数量
    5、在等待慢速I/O操作结束的同时,程序可执行其他的计算任务
    6、计算密集型应用,为了能在多处理器系统上运行,将计算分解到多个线程中实现
    7、I/O密集型应用,为了提高性能,将I/O操作重叠。线程可以同时等待不同的I/O操作。

    线程的缺点

    性能损失

    一个很少被外部事件阻塞的计算密集型线程往往无法与共它线程共享同一个处理器。如果计算密集型线程的数量比可用的处理器多,那么可能会有较大的性能损失,这里的性能损失指的是增加了额外的同步和调度开销,而可用的资源不变。

    健壮性降低

    编写多线程需要更全面更深入的考虑,在一个多线程程序里,因时间分配上的细微偏差或者因共享了不该共享的变量而造成不良影响的可能性是很大的,换句话说线程之间是缺乏保护的。

    缺乏访问控制

    进程是访问控制的基本粒度,在一个线程中调用某些OS函数会对整个进程造成影响。

    编程难度提高

    编写与调试一个多线程程序比单线程程序困难得多。

    创建线程

    #include <pthread.h>
    pthread_create (thread, attr, start_routine, arg) 
    
    参数 描述
    thread 指向线程标识符指针
    attr 一个不透明的属性对象,可以被用来设置线程属性。您可以指定线程属性对象,也可以使用默认值 NULL
    start_routine 线程运行函数起始地址,一旦线程被创建就会执行
    arg 运行函数的参数。它必须通过把引用作为指针强制转换为 void 类型进行传递。如果没有传递参数,则使用 NULL

    创建线程成功时,函数返回 0,若返回值不为 0 则说明创建线程失败。

    终止线程

    #include <pthread.h>
    pthread_exit (status) 
    

    在这里,pthread_exit 用于显式地退出一个线程。通常情况下,pthread_exit() 函数是在线程完成工作后无需继续存在时被调用。
    如果 main() 是在它所创建的线程之前结束,并通过 pthread_exit() 退出,那么其他线程将继续执行。否则,它们将在 main() 结束时自动被终止。

    程序示例

    #include <iostream>
    // 必须的头文件
    #include <pthread.h>
    using namespace std; 
    #define NUM_THREADS 5 
    // 线程的运行函数
    void* say_hello(void* args)
    {
        cout << "Hello Runoob!" << endl;
        return 0;
    }
    int main()
    {
        // 定义线程的 id 变量,多个变量使用数组
        pthread_t tids[NUM_THREADS];
        for(int i = 0; i < NUM_THREADS; ++i)
        {
            //参数依次是:创建的线程id,线程参数,调用的函数,传入的函数参数
            int ret = pthread_create(&tids[i], NULL, say_hello, NULL);
            if (ret != 0)
            {
               cout << "pthread_create error: error_code=" << ret << endl;
            }
        }
        //等各个线程退出后,进程才结束,否则进程强制结束了,线程可能还没反应过来;
        pthread_exit(NULL);
    }
    
    g++ test.cpp -lpthread -o test        #linux编译指令
    
    #include <iostream>
    #include <cstdlib>
    #include <pthread.h>
    using namespace std; 
    #define NUM_THREADS     5
    void *PrintHello(void *threadid)
    {  
       // 对传入的参数进行强制类型转换,由无类型指针变为整形数指针,然后再读取
       int tid = *((int*)threadid);
       cout << "Hello Runoob! 线程 ID, " << tid << endl;
       pthread_exit(NULL);
    }
    int main ()
    {
       pthread_t threads[NUM_THREADS];
       int indexes[NUM_THREADS];// 用数组来保存i的值
       int rc;
       int i;
       for( i=0; i < NUM_THREADS; i++ ){      
          cout << "main() : 创建线程, " << i << endl;
          indexes[i] = i; //先保存i的值
          // 传入的时候必须强制转换为void* 类型,即无类型指针        
          rc = pthread_create(&threads[i], NULL, 
                              PrintHello, (void *)&(indexes[i]));
          if (rc){
             cout << "Error:无法创建线程," << rc << endl;
             exit(-1);
          }
       }
       pthread_exit(NULL);
    }
    main() : 创建线程, 0
    main() : 创建线程, 1
    main() : 创建线程, 2
    main() : 创建线程, 3
    main() : 创建线程, Hello Runoob! 线程 ID, 0
    4
    Hello Runoob! 线程 ID, Hello Runoob! 线程 ID, 3
    Hello Runoob! 线程 ID, 1
    Hello Runoob! 线程 ID, 4
    2
    

    连接和分离线程

    pthread_join (threadid, status) 
    pthread_detach (threadid) 
    

    pthread_join() 子程序阻碍调用程序,直到指定的 threadid 线程终止为止。当创建一个线程时,它的某个属性会定义它是否是可连接的(joinable)或可分离的(detached)。只有创建时定义为可连接的线程才可以被连接。如果线程创建时被定义为可分离的,则它永远也不能被连接。

    #include <iostream>
    #include <cstdlib>
    #include <pthread.h>
    #include <unistd.h>
     
    using namespace std;
     
    #define NUM_THREADS     5
     
    void *wait(void *t)
    {
       int i;
       long tid;
     
       tid = (long)t;
     
       sleep(1);
       cout << "Sleeping in thread " << endl;
       cout << "Thread with id : " << tid << "  ...exiting " << endl;
       pthread_exit(NULL);
    }
     
    int main ()
    {
       int rc;
       int i;
       pthread_t threads[NUM_THREADS];
       pthread_attr_t attr;
       void *status;
       // 初始化并设置线程为可连接的(joinable)
       pthread_attr_init(&attr);
       pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
       for( i=0; i < NUM_THREADS; i++ ){
          cout << "main() : creating thread, " << i << endl;
          rc = pthread_create(&threads[i], NULL, wait, (void *)&i );
          if (rc){
             cout << "Error:unable to create thread," << rc << endl;
             exit(-1);
          }
       } 
       // 删除属性,并等待其他线程
       pthread_attr_destroy(&attr);
       for( i=0; i < NUM_THREADS; i++ ){
          rc = pthread_join(threads[i], &status);
          if (rc){
             cout << "Error:unable to join," << rc << endl;
             exit(-1);
          }
          cout << "Main: completed thread id :" << i ;
          cout << "  exiting with status :" << status << endl;
       } 
       cout << "Main: program exiting." << endl;
       pthread_exit(NULL);
    }
    

    进程的三种基本状态:

    (1) 就绪状态:进程已获得除CPU外的所有必要资源,只等待CPU时的状态。一个系统会将多个处于就绪状态的进程排成一个就绪队列。
    (2) 执行状态:进程已获CPU,正在执行。单处理机系统中,处于执行状态的进程只一个;多处理机系统中,有多个处于执行状态的进程。
    (3) 阻塞状态:正在执行的进程由于某种原因而暂时无法继续执行,便放弃处理机而处于暂停状态,即进程执行受阻。(这种状态又称等待状态或封锁状态)

    进程的操作

    创建进程有两种方式,一是由操作系统创建;二是由父进程创建。操作系统创建的进程,它们之间是平等的,一般不存在资源继承关系。而由父进程创建的进程(子进程),它们和父进程存在隶属关系。子进程又可以创建进程,形成一个进程家族。
    fork()函数调用后有2个返回值,调用一次,返回两次。成功调用fork函数后,当前进程实际上已经分裂为两个进程,一个是原来的父进程,另一个是刚刚创建的子进程。fork()函数的2个返回值,一个是父进程调用fork函数后的返回值,该返回值是刚刚创建的子进程的ID;另一个是子进程中fork函数的返回值,该返回值是0。这样可以用返回值来区分父、子进程。

    进程的编程示例

    #include <stdio.h>
    #include <sys/types.h>
    #include <unistd.h>
    #include <iostream>
    #include <string>
    using namespace std;
    main()
    {
        pid_t pid;
        char *msg;
        int k;
        pid=fork();
        switch(pid){
            //子进程执行部分
    		case 0:
                msg="Child process is running.\n";
                k=3;
                break;
            case -1:
                perror("Process creation failed.\n");
                break;
            //父进程执行部分
    		default:
                msg="Parent process is running.\n";
                k=5;
                break;
        }
    	//父子进程共同执行部分
        while(k>0){
            puts(msg);
            sleep(1);
            k--;
        }
    }
    Parent process is running.
    Child process is running.
    Parent process is running.
    Child process is running.
    Parent process is running.
    Child process is running.
    Parent process is running.
    Parent process is running.
    

    注意事项

    父子进程终止的先后顺序不同会产生不同的结果。

    • 在子进程退出前父进程先退出,则系统会让init进程接管子进程。
    • 当子进程先于父进程终止,而父进程又没有调用wait函数等待子进程结束,子进程进入僵尸状态,并且会一直保持下去除非系统重启。子进程处于僵尸状态时,内核只保存该进程的一些必要信息以备父进程所需。此时子进程始终占用着资源,同时也减少了系统可以创建的最大进程数。如果子进程先于父进程终止,且父进程调用了wait或waitpid函数,则父进程会等待子进程结束。
    • 在Linux下,可以简单地将SIGCHLD信号的操作设为SIG_IGN,这样当子进程结束时就不会称为僵尸进程。
    展开全文
  • 线程是什么?要理解这个概念,须要先了解一下操作系统的一些相关概念。大部分操作系统(如...这样每个任务都能得到执行,由于CPU的执行效率非常高,时间片非常短,在各个任务之间快速地切换,给人的感觉就是多个
  • c++进程、线程、多进程

    千次阅读 2018-10-09 20:51:07
    进程进程是一正在执行的程序,是向CPU申请资源的,进程之间...线程是任务处理的一种特殊形式,任务处理允许让电脑同时运行两或两以上的程序。一般情况下,两种类型的任务处理:基于进程和基于线...
  • 线程和进程都是现在电脑概念里比较时髦的用语,什么是线程,什么是进程?本文详细的给您介绍一下,希望能增进您对当代电脑技术的了解,有不到之处,还往高手予以...若干进程有可能与同一个程序相关系,且每个进程
  • python分布式多进程框架 Ray

    万次阅读 2019-03-13 19:49:27
    我们需要利用多个核心或多台机器来加速应用程序或大规模运行它们。网络爬虫和搜索所使用的基础设施并不是在某人笔记本电脑上运行的单线程程序,而是相互通信和交互的服务的集合。 ray的api接口教程:...
  • Python使用multiprocessing实现多进程

    万次阅读 2019-06-05 22:14:11
    Python使用multiprocessing实现多进程 进程是操作系统进行资源分配和调度的一基本单位。 一、进程任务介绍 程序:程序是一静态的概念。在一台电脑上,我们安装了很程序,这些程序是可以运行的。比如...
  • 进程和线程是计算机软件领域里很重要的概念,进程和线程有区别,也有着密切的联系,先来辨析一下这两概念: 1.定义 进程是具有一定独立功能的程序关于某个数据集合上的一次运行活动,进程是系统进行资源分配和...
  • 10.线程,多进程

    千次阅读 2018-07-23 16:22:37
    线程 线程锁 ThreadLocal 1.系统差异 在Linux和Unix系统上,os模块有fork调用方式来创建子进程,在windows上可用multiprocessing模块来操作进程。 fork方式: import os print('Process (%s...
  • Python多进程写入同一文件

    万次阅读 多人点赞 2016-07-16 10:18:49
    最近用python的正则表达式处理了一些文本数据,需要把结果写到文件里面,但是由于文件比较大,所以运行起来花费的时间很长。...既然线程没有什么用处,那就可以使用多进程处理,毕竟多进程是可以不受
  • python学习——多进程

    千次阅读 2016-07-17 10:41:17
    Python程序实现多进程(multiprocessing),我们先了解操作系统的相关知识。 Unix/Linux操作系统提供了一fork()系统调用,它非常特殊。普通的函数调用,调用一次,返回一次,但是fork()调用一次,返回两次,...
  • Python多进程线程(跑满CPU)

    万次阅读 多人点赞 2019-05-07 15:06:16
    Python多进程线程(跑满CPU) 概念 任务可以理解为进程(process),如打开一word就是启动一word进程。在一word进程之中不只是进行打字输入,还需要拼写检查、打印等子任务,我们可以把进程中的这些子...
  • 在windows环境下的IDE/Pycharm,无法运行多进程的问题 解决: 在CMD下运行既可. 原因: 待解决 cookies: 两方法一起进行的办法: def test(): pritn("1") def test2(): print("2") if __name__...
  • 考虑到现在电脑一般都有双核甚至四核的CPU,故可设计算法并行的方法,通过python编程实现并行运算,从而加快处理的速度。虽然没有GPU进行图像算法的并行运算,但是能利用好双核CPU应该也是大有帮助吧! ...
  • 最近在使用VS和Android Studio(以下简称:as)...首先as没有反应,一会后,电脑会越来越卡,关谁谁没反应,打开任务管理器,发现有很aapt.exe进程(aapt.exe是android开发工具包里一程序,路径:\sdk\build-tools\sd
  • 多核 线程 进程的概念

    千次阅读 2015-05-09 20:13:07
    我们在买电脑的时候经常遇到一些概念,我这电脑是多核线程的,什么双核的,什么四核、八核的,这种运动速度电脑快!那么这样的电脑为什么运行速度快?当然,运行速度快有很原因,比如主频、缓存什么的。这里我们...
  • 为什么浏览器采用多进程模型

    千次阅读 2015-08-14 01:31:38
    但是进程下的进程间通讯(IPC)很慢,而分为进程后,一些协作任务就要分开到两个进程,如何能保持良好的性能,更不说比单进程模型更高的性能了? 所以这里再次探讨浏览器选择进程架构的原因,以及对应架构中的...
  • 最近在使用Pyinstaller打包Python程序的时候发现,打包过程正常,但在运行时会出错,表现为进程不断增加至占满电脑CPU死机,程序版本及环境为: Windows 10 Python3.6 Pyinstaller 3.4 经过网上的多番搜索查阅发现...
  • Python多进程库multiprocessing中进程池Pool类的使用

    万次阅读 多人点赞 2016-09-04 18:27:21
    multiprocessing模块提供process类实现新建进程,可以在Python中實現並行的效果。
  • 什么是线程和多进程

    千次阅读 2014-08-07 13:32:35
    线程和进程都是现在电脑概念里比较时髦的用语,什么是线程,什么是进程?本文详细的给您介绍一下,希望能增进您对当代电脑技术的了解,有不到之 ...若干进程有可能与同一个程序相关系,且每个进程皆可以同步
  • 当然既然是都是 多这个字开头,那么就是多任务,我们需要了解并发:指的是任务数多余cpu核数,通过操作系统的各种任务调度算法, 实现⽤多个任务“⼀起”执⾏(实际上总有⼀些任务不在执⾏,因为切换任 务的速度...
  • python任务(multiprocessing进程

    千次阅读 2019-01-07 10:40:27
    一个程序有多个进程。 2、进程和线程都会执行多任务,但是子进程创建会把主进程的代码与数据复制一部份,这样耗费的资源比较大,但是比单任务效率高。(线程创建是资源共享的,因此耗费的资源少) 2、linux中查看...
  • Linux多进程编程(典藏、含代码)

    千次阅读 2020-02-03 21:44:40
    1.2、多进程(任务)并行的实现 1.3、重要指令 1.4、父子进程进程ID 二、多进程编程 2.1创建子进程 (fork/vfork 叉子) 2.1.1 fork 2.1.2vfork 2.2进程结束 2.2.1正常退出 2.2.2异常退出 2.3等待进程结束并...
  • 多进程线程的实现与原理

    千次阅读 2018-02-12 01:34:34
    多进程 关键字 开启进程的俩种方式 进程之间的内存空间是隔离的 进程中的join方法使用 进程对象的其它属性和方法 守护进程的使用 进程的互斥锁 进程实现队列 生产者和消费者模型 线程 使用线程实现tcp并发...
  • Python实现多进程的方式主要有两种, 一种是使用 os库中的fork方法, 另一种 方法是使用multiprocessing库。这两种方法的区别在千前者仅适用于Unix/Linux操作系统,对Windows 不支持, 后者则是跨平台的实现方式。 ...
  • 为什么浏览器会使用多进程架构。

    万次阅读 多人点赞 2012-08-28 21:12:29
    在使用浏览器时偶然发现虽然仅仅打开一个标签页,但是在任务管理器内发现有多个浏览器进程在运行,占用了不小的内存,当时很纳闷:为什么一个浏览器却需要多个进程呢?不经意间在网上发现这篇文章,以Google chrome...
  • 1.异常处理机制 def test(x): try: y = 10 / x print(y) #except Exception as e: #print(e) #可以打印出异常的类型 except ZeroDivisionError: #抛出异常,执行下面的程序,如果是界面软件可以弹出一...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 95,164
精华内容 38,065
关键字:

如何让电脑处理多个进程