查看: 4383|回复: 5

【懒兔子玩树莓】Python多线程、多进程编程

[复制链接]
  • TA的每日心情
    无聊
    2015-8-17 09:38
  • 签到天数: 361 天

    连续签到: 1 天

    [LV.8]以坛为家I

    发表于 2015-7-10 18:59:40 | 显示全部楼层 |阅读模式
    分享到:
    本帖最后由 懒兔子 于 2015-7-15 20:51 编辑

    1.多任务编程

    除了计算性能和图形显示以外,树莓派区别于Arduino的一大特点就是运行多任务操作系统。通过多任务系统用户可以同时执行多个互相独立的程序(任务),来完成不同的操作。

    利用Python的多任务编程可以方便地实现并行运算,同时充分利用树莓派的多个内核。当然这里面有一些是真的并行操作,还有通过分时轮流使用CPU来实现的“伪并行”。


    1.1.多线程编程

    多线程操作的特点是简单易用,可用于处理CPU占用率不高的任务。虽然一个进程中可以建立多个线程,但由于同一个Python进程只能目前利用一个CPU内核,因此只能利用树莓派25%的CPU资源。这里例举两种多线程模块和threading。thread模块是比较底层的模块,threading模块对thread做了一些包装,以方便调用。需要注意的是Python3.x中已经不再支持thread模块,请使用threading实现多线程操作。

    thread模块编程示例:

    1. import thread
    2. import time

    3. def thread1():
    4.     while True:
    5.         print("\nThis is thread1!")
    6.         time.sleep(1)


    7. def thread2():
    8.     while True:
    9.         print("\nThis is thread2!")
    10.         time.sleep(2)

    11. thread.start_new_thread(thread1, ())
    12. thread.start_new_thread(thread2, ())

    13. while True:
    14.     print("\nThis is the main thread!")
    15.     time.sleep(4)
    复制代码

    运行效果


    threading模块编程示例:

    1. import threading
    2. import time

    3. def my_thread():
    4.     for i in range(0, 4):
    5.         print("\nThis is a sub thread!")
    6.         time.sleep(1)

    7. thread1 = threading.Thread(target = my_thread, args = ())
    8. thread2 = threading.Thread(target = my_thread, args = ())
    9. thread1.start()
    10. thread2.start()
    11. thread1.join()
    12. thread2.join()

    13. print("\nThis is the main thread!")
    复制代码

    代码中的start()函数用于启动线程,join()用于等待线程结束。

    运行效果


    另外thread模块还有一种timer方式,可以延时一段时间再启动线程,代码示例如下:

    1. import threading
    2. import time

    3. def thread_timer():
    4.     print("\nThis is a timer thread!")

    5. thread_tm = threading.Timer(3, thread_timer)
    6. thread_tm.start()

    7. for i in range(0, 5):
    8.     print("\nThis is the main thread!")
    9.     time.sleep(1)
    复制代码

    运行效果




    1.2.Fork进程操作

    我们在上一节中提到了,python的多线程操作只能利用一个CPU内核的资源。如果想要使用树莓派2的全部资源,方法之一就是使用fork创建多个进程。下面的代码演示了简单的fork操作。

    1. import os, time

    2. pid = os.fork()

    3. # New created child process, fork() returns 0
    4. if pid == 0:
    5.     print("I'm the child process, PID = %d" % os.getpid())
    6.     time.sleep(5)
    7.     exit(0)

    8. # Parent process, fork() returns PID of created child
    9. else:
    10.     child_pid = pid
    11.     print("I'm the parent process, PID = %d\n--> My new created child is %d" % (os.getpid(), child_pid))
    12.     time.sleep(1)

    13. print("Waiting for the child...")
    14. os.waitpid(child_pid, 0)
    15. print("Child has finished!")
    复制代码

    与thread和threading不同的是,在这里os.fork()的作用是克隆出一个子进程,也就是不管新建的进程还是原有的进程,都会执行fork后面相同的代码。通过对os.fork()的 返回值即可判断该进程为新建立的子进程(返回0)还是原有的父进程(返回子进程的PID)。

    os.waitpid()则用来等待子进程结束,和thread中的jion()类似。

    图 运行效果


    进程间通信不想线程那样简单,可以使用全局变量。想要在不同的进程间传输信息,就需要使用特有的机制。下面的例子介绍了通过os.pipe()实现两个进程之间通信的方法:

    1. import os, time

    2. # Create pipes for data transmitting
    3. (r, w) = os.pipe()
    4. (r, w) = os.fdopen(r, 'r'), os.fdopen(w, 'w')

    5. pid = os.fork()

    6. # New created child process, fork() returns 0
    7. if pid == 0:
    8.     r.close()
    9.     for i in range(10):
    10.         print("[Child] Sending msg<%d> to parent..." % i)
    11.         w.write("Hello<%d>!\n" % i)
    12.         w.flush()
    13.         time.sleep(1)              


    14. # Parent process, fork() returns PID of created child
    15. else:
    16.     w.close()
    17.     while True:
    18.         msg = r.readline()
    19.         if not msg: break
    20.         print("[Parent] Msg from child[PID%d]: %s" %(pid, msg))
    复制代码

    示例代码中建立了r、w两个通道,并通过w.write()和r.readline()来进行写入和读取操作。同样的使用fork建立子进程,在子进程中写入字符串信息而不需要读取,因此关闭r通道只保留w通道;在父进程中只用读取子进程发来的信息,因此关闭w通道。


    图 运行效果


    1.3.Multiprocessing模块

    对于初学者来说,在使用fork建立多个子进程时可能不容易理清代码关系。除了os.fork()功能以外,Python中还拥有专用于多任务处理的multiprocessing模块。在下面代码中可以看出,multiprocessing在建立process时和前面的threading模块中建立线程的方式类似。

    1. import multiprocessing as mp
    2. import time, os

    3. def my_process(num):
    4.     print("I'm process no.%d, PID = %d." %(num, os.getpid()))
    5.     time.sleep(3)
    6.       
    7. print("Creating new processes...")   
    8. p1 = mp.Process(target=my_process, args=(1,))
    9. p2 = mp.Process(target=my_process, args=(2,))
    10. p3 = mp.Process(target=my_process, args=(3,))

    11. p1.start()
    12. p2.start()
    13. p3.start()

    14. print("Waiting for all the processes...")
    15. p1.join()
    16. p2.join()
    17. p3.join()
    复制代码

    图 运行效果


    multiprocessing中也集成了pipe功能,用于进程间的通信。同时我们还可以使用队列(queue)来实现不同进程的数据传输。下面代码建立了reader_p子进程用来读取queue中的数据,并在父进程中向queue中写入数据。与pipe不同,代码中的两个进程共享同一个queue。而写入和读取数据的函数也变成了put()和get()。

    1. import multiprocessing as mp
    2. import time

    3. # Read msg from the queue and print it out
    4. def reader(queue):
    5.     while True:
    6.         msg = queue.get()
    7.         if (msg == 'DONE'):
    8.             break
    9.         print("[Reader]Get msg from writer: %s" % msg)

    10. # Write msg with number into the queue
    11. def writer(queue):
    12.     print("[Writer] Sending msg to reader...")
    13.     for i in range(0, 5):
    14.         queue.put("Hello <%d>!" % i)
    15.         time.sleep(1)
    16.     queue.put('DONE')

    17. queue = mp.Queue()

    18. print("Create new process as reader!")                  
    19. reader_p = mp.Process(target=reader, args=((queue),))
    20. reader_p.daemon = True
    21. reader_p.start()

    22. writer(queue)
    23. reader_p.join()
    复制代码

    程序运行后,reader_p进程开始监控队列中的数据,知道接收到字符串“DONE”后退出。父进程向队列中发送一组数据,最后等待子进程结束后退出。

    图 运行效果


    回复

    使用道具 举报

  • TA的每日心情
    开心
    2016-8-15 09:30
  • 签到天数: 162 天

    连续签到: 1 天

    [LV.7]常住居民III

    发表于 2015-7-13 08:52:38 | 显示全部楼层
    支持兔子,强帖
    回复 支持 反对

    使用道具 举报

  • TA的每日心情
    开心
    2014-4-21 09:44
  • 签到天数: 26 天

    连续签到: 1 天

    [LV.4]偶尔看看III

    发表于 2015-7-13 09:13:46 | 显示全部楼层
    占楼学习
    回复 支持 反对

    使用道具 举报

  • TA的每日心情
    开心
    2019-11-30 19:48
  • 签到天数: 981 天

    连续签到: 1 天

    [LV.10]以坛为家III

    发表于 2015-7-13 09:16:09 | 显示全部楼层
    感谢楼主!!!!!!!!
    回复 支持 反对

    使用道具 举报

    该用户从未签到

    发表于 2015-7-13 09:41:24 | 显示全部楼层
    python 的多线程无法利用多核 CPU。想利用 树莓派2 的多核,可以使用多进程并行
    回复 支持 反对

    使用道具 举报

  • TA的每日心情

    2023-7-25 22:49
  • 签到天数: 385 天

    连续签到: 1 天

    [LV.9]以坛为家II

    发表于 2015-8-14 13:49:28 | 显示全部楼层
    最近也在学习python
    回复 支持 反对

    使用道具 举报

    您需要登录后才可以回帖 注册/登录

    本版积分规则

    关闭

    站长推荐上一条 /2 下一条



    手机版|小黑屋|与非网

    GMT+8, 2024-12-24 21:51 , Processed in 0.160364 second(s), 24 queries , MemCache On.

    ICP经营许可证 苏B2-20140176  苏ICP备14012660号-2   苏州灵动帧格网络科技有限公司 版权所有.

    苏公网安备 32059002001037号

    Powered by Discuz! X3.4

    Copyright © 2001-2024, Tencent Cloud.