Day-12: 进程和线程

时间:2022-09-25 20:40:49
  • 进程和线程

  在操作系统看来,一个任务就是一个进程,而一个进程内部如果要做多个任务就是有多个线程。一个进程至少有一个线程。

  真正的并行执行任务是由多个CUP分别执行任务,实际中是由,操作系统轮流让各个任务交替执行,任务1执行0.01秒,任务2执行0.01秒,之后再依次切换。

  Python中支持两种模式:

  多进程模式

  多线程模式

  • 多进程

  Linux操作系统下,提供了一个fork()系统调用。调用一次fork(),返回两次,因为操作系统自动把当前的进程(作为父进程)复制了一份(称为子进程),然后子进程返回0,父进程返回子进程的ID。

# 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()调用,提供了multiprocessing模块进行跨平台版本的多进程模块。

用Process类代表创建进程对象,传入一个执行函数和函数的参数。之后再用start()方法启动,jion()方法可以等待子进程结束后再继续往下进行,通常用于进程间的同步。

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.

  Pool进程池创建多个子进程

  对Pool对象创建多个子进程后,用close()方法结束创建,再用join()方法等待所有子进程执行完毕。在每个子进程中会随机休眠一段时间,其他的子进程在这段休眠时间里就会调用。

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.

  进程间通信

  Python的miltiprocessing模块包装了底层的机制,提供了Queue、Pipes等多种方式来交换数据。

  在父进程中创建两个子进程,一个往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.
  • 多线程

  Python中提供两个模块,thread是低级模块,threading是高级模块,对thread进行了封装。

import time, threading

# 新线程执行的代码:
def loop():
print 'thread %s is running...' % threading.current_thread().name
n = 0
while n < 5:
n = n + 1
print 'thread %s >>> %s' % (threading.current_thread().name, n)
time.sleep(1)
print 'thread %s ended.' % threading.current_thread().name print 'thread %s is running...' % threading.current_thread().name
t = threading.Thread(target=loop, name='LoopThread')
t.start()
t.join()
print 'thread %s ended.' % threading.current_thread().name
thread MainThread is running...
thread LoopThread is running...
thread LoopThread >>> 1
thread LoopThread >>> 2
thread LoopThread >>> 3
thread LoopThread >>> 4
thread LoopThread >>> 5
thread LoopThread ended.
thread MainThread ended.

  多进程和多线程最大的不同在于,多进程中,同一个变量,各自有一份拷贝存在于每个进程中,互不影响,而多线程中,所有变量都由所有线程共享,所以,任何一个变量都可以被任何一个线程修改。因此,线程之间共享数据最大的危险在于多个线程同时该变一个变量,把内容给改乱了。  

  因此得加上一把锁lock

balance = 0
lock = threading.Lock() def run_thread(n):
for i in range(100000):
# 先要获取锁:
lock.acquire()
try:
# 放心地改吧:
change_it(n)
finally:
# 改完了一定要释放锁:
lock.release()

  当多个线程同时执行lock.acquire()时,只有一个线程能成功地获取锁,然后继续执行代码,其他线程就继续等待直到获得锁为止。

  但是,这样实际上就不是并行处理了。

  Python的多进程由于存在GIL锁的问题,所以多线程实际上不能有效利用多核。多线程的并发在Python中是无用的。

  • ThreadLocal

  全局变量local_school就是一个ThreadLoacl对象,每个Thread对它都可以读写student属性,但是互不影响。可以把local_school看成全局变量,但每个属性如local_school.student都是线程的局部变量,可以任意读写而互不干扰,也不用管理锁的问题,ThreadLocal内部会处理。

import threading

# 创建全局ThreadLocal对象:
local_school = threading.local() def process_student():
print 'Hello, %s (in %s)' % (local_school.student, threading.current_thread().name) def process_thread(name):
# 绑定ThreadLocal的student:
local_school.student = name
process_student() t1 = threading.Thread(target= process_thread, args=('Alice',), name='Thread-A')
t2 = threading.Thread(target= process_thread, args=('Bob',), name='Thread-B')
t1.start()
t2.start()
t1.join()
t2.join()
Hello, Alice (in Thread-A)
Hello, Bob (in Thread-B)
  • 进程vs线程

  多进程的优点是稳定性高,一个崩溃,不会影响其他的进程,但是,代价大,在linux下,调用fork还可以,但是windows下进程开销巨大。

  多线程模式比多进程快一点,但是也快不了多少,缺点十分明显,由于共享进程的内存,一个线程崩了,就都崩了。

  计算密集型和IO密集型:

  计算密集型会消耗大量的CPU资源,代码的运行效率就至关重要,Python等脚本语言运行效率低,不适合。

  IO密集型涉及到网络、磁盘IO的任务,它们的CUP消耗较少,任务的主要时间在等待IO操作完成,CUP效率无法完全使用,所以适合开发效率高的语言。

  现代操作系统对IO操作进行了巨大的改进,支持异步IO。利用异步IO,就可以用单进程模型来执行多任务,这种全新的模型称为事件驱动型。

  • 分布式进程

  多台电脑协助工作,一台电脑作为调度者,依靠网络通信,将任务分布到其他电脑的进程中。

  通过manager模块把Queue通过网络暴露出去,让其他机器的进程可以访问Queue

服务器继承中,负责启动Queue,把Queue注册到网络上,然后往Queue里面写入任务:

# taskmanager.py

import random, time, Queue
from multiprocessing.managers import BaseManager # 发送任务的队列:
task_queue = Queue.Queue()
# 接收结果的队列:
result_queue = Queue.Queue() # 从BaseManager继承的QueueManager:
class QueueManager(BaseManager):
pass # 把两个Queue都注册到网络上, callable参数关联了Queue对象:
QueueManager.register('get_task_queue', callable=lambda: task_queue)
QueueManager.register('get_result_queue', callable=lambda: result_queue)
# 绑定端口5000, 设置验证码'abc':
manager = QueueManager(address=('', 5000), authkey='abc')
# 启动Queue:
manager.start()
# 获得通过网络访问的Queue对象:
task = manager.get_task_queue()
result = manager.get_result_queue()
# 放几个任务进去:
for i in range(10):
n = random.randint(0, 10000)
print('Put task %d...' % n)
task.put(n)
# 从result队列读取结果:
print('Try get results...')
for i in range(10):
r = result.get(timeout=10)
print('Result: %s' % r)
# 关闭:
manager.shutdown()

在另一台机器上启动任务进程:

# taskworker.py

import time, sys, Queue
from multiprocessing.managers import BaseManager # 创建类似的QueueManager:
class QueueManager(BaseManager):
pass # 由于这个QueueManager只从网络上获取Queue,所以注册时只提供名字:
QueueManager.register('get_task_queue')
QueueManager.register('get_result_queue') # 连接到服务器,也就是运行taskmanager.py的机器:
server_addr = '127.0.0.1'
print('Connect to server %s...' % server_addr)
# 端口和验证码注意保持与taskmanager.py设置的完全一致:
m = QueueManager(address=(server_addr, 5000), authkey='abc')
# 从网络连接:
m.connect()
# 获取Queue的对象:
task = m.get_task_queue()
result = m.get_result_queue()
# 从task队列取任务,并把结果写入result队列:
for i in range(10):
try:
n = task.get(timeout=1)
print('run task %d * %d...' % (n, n))
r = '%d * %d = %d' % (n, n, n*n)
time.sleep(1)
result.put(r)
except Queue.Empty:
print('task queue is empty.')
# 处理结束:
print('worker exit.')

服务进程启动如下:

$ python taskmanager.py
Put task 3411...
Put task 1605...
Put task 1398...
Put task 4729...
Put task 5300...
Put task 7471...
Put task 68...
Put task 4219...
Put task 339...
Put task 7866...
Try get results...

工作进程启动如下:

$ python taskworker.py 127.0.0.1
Connect to server 127.0.0.1...
run task 3411 * 3411...
run task 1605 * 1605...
run task 1398 * 1398...
run task 4729 * 4729...
run task 5300 * 5300...
run task 7471 * 7471...
run task 68 * 68...
run task 4219 * 4219...
run task 339 * 339...
run task 7866 * 7866...
worker exit.

等到工作进程结束后,服务进程如下:

Result: 3411 * 3411 = 11634921
Result: 1605 * 1605 = 2576025
Result: 1398 * 1398 = 1954404
Result: 4729 * 4729 = 22363441
Result: 5300 * 5300 = 28090000
Result: 7471 * 7471 = 55815841
Result: 68 * 68 = 4624
Result: 4219 * 4219 = 17799961
Result: 339 * 339 = 114921
Result: 7866 * 7866 = 61873956

  注意Queue的作用是来传递任务和接受结果的,每个任务的描述量要尽量小。比如发送一个处理日志文件的任务,不要发送几百兆的日志文件本身,而是发送日志文件存放的完整路径,由Worker进程再去共享的磁盘上读取文件。

注:本文为学习廖雪峰Python入门整理后的笔记

Day-12: 进程和线程的更多相关文章

  1. python基础-12 多线程queue 线程交互event 线程锁 自定义线程池 进程 进程锁 进程池 进程交互数据资源共享

    Python中的进程与线程 学习知识,我们不但要知其然,还是知其所以然.你做到了你就比别人NB. 我们先了解一下什么是进程和线程. 进程与线程的历史 我们都知道计算机是由硬件和软件组成的.硬件中的CP ...

  2. 12 并发编程-&lpar;线程&rpar;-线程queue&amp&semi;进程池与线程池

    queue 英 /kjuː/ 美 /kju/ 队列 1.class queue.Queue(maxsize=0) #队列:先进先出 import queue q=queue.Queue() q.put ...

  3. python之进程与线程

    什么是操作系统       可能很多人都会说,我们平时装的windows7 windows10都是操作系统,没错,他们都是操作系统.还有没有其他的? 想想我们使用的手机,Google公司的Androi ...

  4. python 进程和线程

    python中的进程.线程(threading.multiprocessing.Queue.subprocess) Python中的进程与线程 学习知识,我们不但要知其然,还是知其所以然.你做到了你就 ...

  5. Python&lowbar;Day10&lowbar;进程、线程、协程

    本节内容    操作系统发展史介绍    进程.与线程区别    python GIL全局解释器锁    线程        语法        join        线程锁之Lock\Rlock\ ...

  6. Python之路&comma;Day9&comma; 进程、线程、协程篇

    本节内容 操作系统发展史介绍 进程.与线程区别 python GIL全局解释器锁 线程 语法 join 线程锁之Lock\Rlock\信号量 将线程变为守护进程 Event事件 queue队列 生产者 ...

  7. python中的进程、线程(threading、multiprocessing、Queue、subprocess)

    Python中的进程与线程 学习知识,我们不但要知其然,还是知其所以然.你做到了你就比别人NB. 我们先了解一下什么是进程和线程. 进程与线程的历史 我们都知道计算机是由硬件和软件组成的.硬件中的CP ...

  8. Python进程、线程、协程

    进程和线程的解释 进程(process)和线程(thread)是操作系统的基本概念,计算机的核心是CPU,它承担了所有的计算任务: 单个CPU一次只能运行一个任务,代表单个CPU总是运行一个进程,其他 ...

  9. Queue、进程、线程、协程

    参考博客地址 http://www.cnblogs.com/alex3714/articles/5230609.html 1.python GIL全局解释器锁 python调用的操作系统的原生线程,当 ...

  10. java核心知识点学习----并发和并行的区别&comma;进程和线程的区别&comma;如何创建线程和线程的四种状态&comma;什么是线程计时器

    多线程并发就像是内功,框架都像是外功,内功不足,外功也难得精要. 1.进程和线程的区别 一个程序至少有一个进程,一个进程至少有一个线程. 用工厂来比喻就是,一个工厂可以生产不同种类的产品,操作系统就是 ...

随机推荐

  1. Untiy3D开发小贴士 OnEnabled与OnDisabled

    设某个组件为NewBehaviour OnEnabled被调用的情况: 1.GameObject添加组件的时候,即AddComponet<NewBehaviour> : 2.包含3.已添加 ...

  2. IOS系统基础知识

    在iOS应用中,每个程序得main函数中都调用了UIApplicationMain函数. 1 2 3 4 5 6 int main(int argc, char *argv[])  {      @a ...

  3. oracle数据迁移、备份等

    exp db  或者数据泵. 或者ETL工具

  4. html5 canvas画板

    点击查看演示地址 <!DOCTYPE HTML> <html> <title>HTML5-CanvasDrawDemo</title> <meta ...

  5. Android little error records

    1:在清单文件(Manifest)中加入权限时要注意,如下例: <uses-permission android:name="android.permission.CHANGE_NET ...

  6. struts2学生信息管理系统篇章②进度报告篇章

    之前做这个系统的时候是什么都不懂的! 经过一个月的时间,慢慢的java的知识都捡起来了. 对struts2和mvc模式都有一一定程度的了解,汇报一下上次的进度. 这个系统我所有的功能中我暂时只做到了下 ...

  7. java分布式开发,什么是分布式开发

    就是同一个服务,把数据库的不同部分分开建立到不同的服务器上.以缓解数据库大量数据访问的压力.很多大公司的业务量比较大,每天的访问量都达到几百万上千万,甚至上亿的访问量,在访问量不是很大的情况下,是可以 ...

  8. 2292&colon; 【POJ Challenge 】永远挑战

    2292: [POJ Challenge ]永远挑战 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 553  Solved: 230[Submit][ ...

  9. jQuery之事件和批量操作、事件委托示例

    一.常用事件 click(function(){...}) // 点击时触发 focus(function(){...}) // 获得焦点触发 blur(function(){...}) // 失去焦 ...

  10. 面向对象【day07】:知识点回顾(十一)

    本节内容 1.self关键字 2.封装 3.继承 4.静态方法 一.self关键字 作用:调用当前方法的对象 1 2 3 4 5 6 7 8 9 10 11 12 13 14 class Foo:   ...