Python 多线程及经过

threading使用 (工业风案例)

import threading
from time import sleep, ctime


loop = [4, 2]


class ThreadFunc:

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

    def loop(self, nloop, nsec):
        '''
        :param nloop: loop函数的名称
        :param nsec: 系统休眠时间
        :return:
        '''
        print('Start loop ', nloop, 'at ', ctime())
        sleep(nsec)
        print('Done loop ', nloop, ' at ', ctime())


def main():
    print("Starting at: ", ctime())

    # ThreadFunc("loop").loop 跟一下两个式子相等:
    # t = ThreadFunc("loop")
    # t.loop
    # 以下t1 和  t2的定义方式相等
    t = ThreadFunc("loop")
    t1 = threading.Thread( target = t.loop, args=("LOOP1", 4))
    # 下面这种写法更西方人,工业化一点
    t2 = threading.Thread( target = ThreadFunc('loop').loop, args=("LOOP2", 2))

    # 常见错误写法
    #t1 = threading.Thread(target=ThreadFunc('loop').loop(100,4))
    #t2 = threading.Thread(target=ThreadFunc('loop').loop(100,2))

    t1.start()
    t2.start()

    t1.join( )
    t2.join()

    print("ALL done at: ", ctime())


if __name__ == '__main__':
    main()

输出>>>

Starting at: Sun Sep 2 10:04:47 2018
Start loop LOOP1 at Sun Sep 2 10:04:47 2018
Start loop LOOP2 at Sun Sep 2 10:04:47 2018
Done loop LOOP2 at Sun Sep 2 10:04:49 2018
Done loop LOOP1 at Sun Sep 2 10:04:51 2018
ALL done at: Sun Sep 2 10:04:51 2018

解析:注意:实例化时threading.Thread(target=xxx,
args=(xxx,))格式完整,工业风写法为init了的类的函数,args为其它param,一行搞定喵

  1. 可以由此安装守护线程,使不首要线程同主线程一起截止

    t1 = threading.Thread(target=fun, args=() )
    # 社会守护线程的方法,必须在start之前设置,否则无效
    t1.setDaemon(True)
    #t1.daemon = True
    t1.start()
    
  2. threading.Lock()的五个线程,竞争资源都acquire(),造成不可以release(),最终不能持续程序。

  3. threading.Semaphore(n)n=允许同时运行线程数

  4. threading.Timer(t, func)指定时间先河线程

在切切实实社会,大家经常索要一种情景,就是同时有六个工作需要履行,如在浏览网页的还要需要听音乐。比如说在舞蹈的时候要唱歌。
如出一辙的,在程序中大家也说不定需要这种景观。如下面大家以同时听音乐和浏览网页为例。

多进程

  1. multiprocessing.Process()直白扭转过程

  2. 创制子类生成:

    import multiprocessing
    from time import sleep, ctime
    
    class ClockProcess(multiprocessing.Process):
        '''
        两个函数比较重要
        1. init构造函数
        2. run
        '''

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

        def run(self):
            while True:
                print("The time is %s" % ctime())
                sleep(self.interval)


    if __name__ == '__main__':
        p = ClockProcess(3)
        p.start()

        while True:
            print('sleeping.......')
            sleep(1)

注意:

1.  `__init__`里用`super().__init__`

2.  重写`run()`

3.  可以用`os.getppid()`得到父进程id,用`os.getpid()`得到本进程id

4.  建立进程:

        q = multiprocessing.JoinableQueue()
        # 运行消费者进程
        cons_p = multiprocessing.Process (target = consumer, args = (q,))
        cons_p.daemon = True
        cons_p.start()

example:

import multiprocessing
from time import ctime


def consumer(input_q):
    print("Into consumer:", ctime())
    while True:
        item = input_q.get()
        if item is None:
            break
        print("pull", item, "out of q")
    print("Out of consumer:", ctime())


def producer(sequence, output_q):
    for item in sequence:
        print("Into procuder:", ctime())
        output_q.put(item)
        print("Out of procuder:", ctime())


if __name__ == '__main__':
    q = multiprocessing.Queue()
    cons_p1 = multiprocessing.Process(target=consumer, args=(q,))
    cons_p1.start()

    cons_p2 = multiprocessing.Process (target=consumer, args=(q,))
    cons_p2.start()

    sequence = [1, 2, 3, 4]
    producer(sequence, q)

    q.put(None)
    q.put(None)

    cons_p1.join()
    cons_p2.join()

Into procuder: Tue Sep 4 15:57:37 2018
Out of procuder: Tue Sep 4 15:57:37 2018
Into procuder: Tue Sep 4 15:57:37 2018
Out of procuder: Tue Sep 4 15:57:37 2018
Into procuder: Tue Sep 4 15:57:37 2018
Out of procuder: Tue Sep 4 15:57:37 2018
Into procuder: Tue Sep 4 15:57:37 2018
Out of procuder: Tue Sep 4 15:57:37 2018
Into consumer: Tue Sep 4 15:57:37 2018
pull 1 out of q
pull 2 out of q
pull 3 out of q
pull 4 out of q
Out of consumer: Tue Sep 4 15:57:37 2018
Into consumer: Tue Sep 4 15:57:37 2018
Out of consumer: Tue Sep 4 15:57:37 2018

分析:

  1. multiprocessing.Queue()树立一个经过间队列
  2. Queue.put()以及Queue.get()为队列操作,先进先出
def network():   
   while True:
    print("正在上网~~~")        
    time.sleep(1)def sing():    
  while True:        
    print("正在听歌……")        
    time.sleep(1)
if __name__ == "__main__":    
  network()    
  sing()

履行代码,我们发现代码并从未如约我们的想法执行,而是,从来进行上网的代码,这样就不可以形成大家想要的功用。
万一你想要同时听音乐和浏览网页两件事,就需要一个新的法门来成功,这么些办法就是—-多任务。

多任务是咋回事呢?

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

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()。

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()方法可以得到该线程的父级编号。

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

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()方法可以拿到该线程的父级编号。

多进程修改全局变量

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("第四个线程")

结果如下

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

我们发现,主线程和子线程各执行了两次,可是第三个和第三个都进行了五次,为何了,看下边的图。

图片 1

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())

结果如下

图片 2

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方法表示阻塞式执行任务,此时就是单任务执行了(一般不会使用,特殊现象才会利用)

发表评论

电子邮件地址不会被公开。 必填项已用*标注

网站地图xml地图