Python:多进程和多线程编程

多进程

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

os.fork():创建子进程
import os

print('process is start...', os.getpid())
child = os.fork()
if child == 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(), child))

# 输出
process is start... 22300
I (22300) just created a child process (22301).
I am child process (22301) and my parent is 22300.

multiprocessing(多进程模块)

multiprocessing模块就是跨平台版本的多进程模块。multiprocessing模块提供了一个Process类来代表一个进程对象。

from multiprocessing import Process
# 启动一个子进程并等待其结束
# 子进程执行的代码
def child_do(name):
    print('I am child process, my name is %s (%s)...' % (name, os.getpid()))

if __name__ == '__main__':
    print('parent process %s.' % os.getpid())
    child = Process(target=child_do, args=('test',))
    print('child process will start.')
    child.start()  # start()方法启动
    child.join()  # join()方法可以等待子进程结束后再继续往下运行,通常用于进程间的同步
    print('child process end.')

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

from multiprocessing import Pool
import os, time, random
def task(name):
    print('running task %s(%s)' % (name, os.getpid()))
    start = time.time()
    time.sleep(random.random() * 3)
    end = time.time()
    print('task %s run %0.2f seconds.' % (name, end - start))

if __name__ == '__main__':
    print('process is start %s' % os.getpid())
    p = Pool(4)
    for i in range(5):
        p.apply_async(task, args=(i,))
    print('waiting for all subprocesses done...')
    p.close()  # 对Pool对象调用join()方法会等待所有子进程执行完毕,调用join()之前必须先调用close(),调用close()之后就不能继续添加新的Process了
    p.join()
    print('all subprocesses done')

上述代码中的pool.apply_async()是apply()函数的变体,apply_async()是apply()的并行版本,apply()是apply_async()的阻塞版本,使用apply()主进程会被阻塞直到函数执行结束,所以说是阻塞版本。apply()既是Pool的方法,也是Python内置的函数,两者等价。

子进程

很多时候,子进程并不是自身,而是一个外部进程。我们创建了子进程后,还需要控制子进程的输入和输出
subprocess模块可以让我们非常方便地启动一个子进程,然后控制其输入和输出

# puthon在代码中运行命令
print('$ nslookup www.python.org')
r = subprocess.call(['nslookup', 'www.python.org'])
print('Exit code:', r)

进程间通信

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

# 写进程
def write(q):
    print('Writing Process %s', os.getpid())
    for value in ['A', 'B', 'C']:
        print('put %s in queue' % value)
        q.put(value)  # 放入队列
        time.sleep(random.random())

# 读进程
def read(q):
    print('Reading Process %s', os.getpid())
    while True:
        value = q.get()  # 从队列中拿
        print('get %s in queue' % value)

if __name__ == '__main__':
    # 父进程创建Queue,并传给各个子进程
    q = Queue()
    # 传给写进程
    pw = Process(target=write, args=(q,))  # 执行的函数式write,参数传递的是这个队列
    # 传给读进程
    pr = Process(target=read, args=(q,))
    # 启动写进程
    pw.start()
    # 启动读进程
    pr.start()
    # 等待写进程完成
    pw.join()
    # 读进程是死循环,无法等待他结束,只能强行终止
    pr.terminate()

多线程

启动一个线程就是把一个函数传入并创建Thread实例,然后调用start()开始执行

创建一个线程
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 end', threading.current_thread().name)

print('thread %s is running' % threading.current_thread().name)  # 由于任何进程默认就会启动一个线程,我们把该线程称为主线程
t = threading.Thread(target=loop, name='LoopThread')  # 创建了一个叫LoopThread的线程,执行的函数式loop
t.start()  # 启动线程
t.join()  # 等待线程
print('thread %s end.' % threading.current_thread().name)  # threading.current_thread()返回当前线程的实例
给线程加锁
balance = 0
lock = threading.Lock()  # 创建一个锁叫lock

def change_it(n):
    # 先存后取,结果应该为0:
    global balance
    balance = balance + n
    balance = balance - n

def run_thread(n):
    for i in range(1000000):
        lock.acquire()  # 获取锁
        try:
            change_it(n)
        finally:
            lock.release()  # 释放锁(最后一定要释放锁)

t1 = threading.Thread(target=run_thread, args=(5,))
t2 = threading.Thread(target=run_thread, args=(8,))
t1.start()
t2.start()
t1.join()
t2.join()
print(balance)

当多个线程同时执行lock.acquire()时,只有一个线程能成功地获取锁,然后继续执行代码,其他线程就继续等待直到获得锁为止
我们用try…finally来确保锁一定会被释放

ThreadLocal

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

# 创建全局ThreadLocal对象:
local_school = threading.local()

def process_student():
    # 获取当前线程关联的student:
    std = local_school.student
    print('Hello, %s (in %s)' % (std, 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()
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值