Python学习笔记#12 内存管理机制与多线程

这篇博客探讨了Python的内存管理机制,包括垃圾回收的引用计数和分代收集,以及如何查看引用计数。同时,文章详细介绍了Python的多线程,包括进程的概念、创建与通信,线程的并发执行以及协程的协同多任务特性,最后展示了如何在Python中实现协程和数据通信。

1.内存管理
垃圾回收机制 :
(1) 以引用计数为主, 分代收集为辅
ps: 引用计数的缺陷是循环引用的问题
(2) 如果一个对象的引用数为0, Python虚拟机就会回收这个对象的内存

垃圾回收 :
(1) 满足特定条件, 自动启动垃圾回收
(2) 当Python运行时, 会记录其中分配对象和取消分配对象的次数
(3) 当两者的差值高于某个阈值时, 垃圾回收才会启动
(4) 查看阈值: gc.get_threshold()

分代回收 :
(1) Python将所有对象分为0、1、2 三代
(2) 所有的新建对象都是0代对象
(3) 当某一代对象经历过垃圾回收, 依然存活, 那么它就被归入下一代对象

引用计数 :
(1) 每个对象都存有指向该对象的引用总数
(2) 查看某个对象的引用计数:
sys.getrefcount()
(3) 可以用del关键字删除某个引用

2.Python多线程
1-进程 :
(1) 是一个执行中的程序
(2) 每个进程都拥有自己的地址空间、内存、数据栈以及其他用于跟踪执行的辅助数据
(3) 操作系统管理其上所有进程的执行, 并为这些进程合理地分配时间
(4) 进程也可通过派生(fork 或 spawn)新的进程来执行任务

实现一个进程 :
(1) 使用multiprocessing实现多进程代码
(2) 用multiprocessing.Process创建进程
(3) start() 启动进程、join() 挂起进程
(4) os.getpid() 获得进程ID

进程之间的通信 :
通过Queue、Pipes等实现进程之间的通信
例:

import time
import random
from multiprocessing import Process, Queue, current_process

class WriteProcess(Process):
    """ 要写的内容 """
    def __init__(self, q, *args, **kwargs):
        self.q = q
        super().__init__(*args, **kwargs)

    def run(self):
        """ 实现进程的业务逻辑 """
        # 要写的内容
        ls = [
            "第1行内容",
            "第2行内容",
            "第3行内容"
        ]
        for line in ls:
            print('写入内容: {0}-{1}'.format(line, current_process().name))
            self.q.put(line)
            # 每写入一次,休息1-5秒
            time.sleep(random.randint(1, 5))

class ReadProcess(Process):
    """ 读取内容进程 """
    def __init__(self, q, *args, **kwargs):
        self.q = q
        super().__init__(*args, **kwargs)

    def run(self):
        while True:
            content = self.q.get()
            print('读取到的内容: {0}-{1}'.format(content, current_process().name))

if __name__ == '__main__':
    # 通过Queue共享数据
    q = Queue()
    t_write = WriteProcess(q)
    t_write.start()
    # 读取进程
    t_read = ReadProcess(q)
    t_read.start()
    t_write.join()
    # 因为读的进程是死循环, 所以需要强行终止
    t_read.terminate()

输出:

写入内容: 第1行内容-WriteProcess-1
读取到的内容: 第1行内容-ReadProcess-2
写入内容: 第2行内容-WriteProcess-1
读取到的内容: 第2行内容-ReadProcess-2
写入内容: 第3行内容-WriteProcess-1
读取到的内容: 第3行内容-ReadProcess-2

使用进程池优化:

import random
import time
from multiprocessing import current_process
from multiprocessing.pool import Pool

def run(file_name, num):
    """ 往文件中写入数据 """
    # 当前的进程
    now_process = current_process()
    # 写入的内容
    with open(file_name, 'a+', encoding='utf-8') as f:
        content = '{0} - {1} - {2}'.format(
            now_process.name,
            now_process.pid,
            num
        )
        f.write(content)
        f.write('\n')
        # 写完随机休息1-3秒
        time.sleep(random.randint(1, 3))
        print(content)

    return 'ok'

if __name__ == '__main__':
    file_name = 'test_pool.txt'
    # 进程池
    pool = Pool(2)
    rest_list = []
    for i in range(20):
        # 同步添加任务
        # rest = pool.apply(run, args=(file_name, i))
        # 异步添加任务
        rest = pool.apply_async(run, args=(file_name, i))
        rest_list.append(rest)
        print('{0} --- {1}'.format(i, rest))
    # 关闭池子
    pool.close()
    pool.join()
    # 查看异步执行的结果
    print(rest_list[0].get())

输出结果:

0 --- <multiprocessing.pool.ApplyResult object at 0x0000017147352A20>
1 --- <multiprocessing.pool.ApplyResult object at 0x0000017147352B00>
2 --- <multiprocessing.pool.ApplyResult object at 0x0000017147352B70>
3 --- <multiprocessing.pool.ApplyResult object at 0x0000017147352C88>
4 --- <multiprocessing.pool.ApplyResult object at 0x0000017147352D30>
5 --- <multiprocessing.pool.ApplyResult object at 0x0000017147352DD8>
6 --- <multiprocessing.pool.ApplyResult object at 0x0000017147352E80>
7 --- <multiprocessing.pool.ApplyResult object at 0x0000017147352F28>
8 --- <multiprocessing.pool.ApplyResult object at 0x0000017147352FD0>
9 --- <multiprocessing.pool.ApplyResult object at 0x0000017147352BA8>
10 --- <multiprocessing.pool.ApplyResult object at 0x000001714736A128>
11 --- <multiprocessing.pool.ApplyResult object at 0x000001714736A198>
12 --- <multiprocessing.pool.ApplyResult object at 0x000001714736A278>
13 --- <multiprocessing.pool.ApplyResult object at 0x000001714736A320>
14 --- <multiprocessing.pool.ApplyResult object at 0x000001714736A400>
15 --- <multiprocessing.pool.ApplyResult object at 0x000001714736A4A8>
16 --- <multiprocessing.pool.ApplyResult object at 0x000001714736A550>
17 --- <multiprocessing.pool.ApplyResult object at 0x000001714736A5F8>
18 --- <multiprocessing.pool.ApplyResult object at 0x000001714736A6A0>
19 --- <multiprocessing.pool.ApplyResult object at 0x000001714736A748>
SpawnPoolWorker-1 - 3500 - 0
SpawnPoolWorker-1 - 3500 - 2
SpawnPoolWorker-2 - 7676 - 1
SpawnPoolWorker-2 - 7676 - 4
SpawnPoolWorker-1 - 3500 - 3
SpawnPoolWorker-2 - 7676 - 5
SpawnPoolWorker-1 - 3500 - 6
SpawnPoolWorker-1 - 3500 - 8
SpawnPoolWorker-2 - 7676 - 7
SpawnPoolWorker-1 - 3500 - 9
SpawnPoolWorker-2 - 7676 - 10
SpawnPoolWorker-1 - 3500 - 11
SpawnPoolWorker-2 - 7676 - 12
SpawnPoolWorker-1 - 3500 - 13
SpawnPoolWorker-1 - 3500 - 15
SpawnPoolWorker-2 - 7676 - 14
SpawnPoolWorker-1 - 3500 - 16
SpawnPoolWorker-1 - 3500 - 18
SpawnPoolWorker-2 - 7676 - 17
SpawnPoolWorker-1 - 3500 - 19
ok

2-线程
(1) 在同一个进程下执行, 并共享相同的上下文
(2) 一个进程中的各个线程与主线程共享同一片数据空间
(3) 线程包括开始、执行顺序和结束3个部分
(4) 它可以被抢占(中断)和临时挂起(睡眠)——让步
(5) 一般是以并发方式执行
PS: 并发
1. 不等同于并行。并行只是并发问题的可能方法之一
2. 是一种属性 : 程序、算法或问题的属性
3. 如果两个事件互不影响, 则两个事件是并发的

实现一个线程 :
(1) 用threading模块代替thread模块
(2) 用threading.Thread创建线程
(3) strat()启动线程
(4) join()挂起线程
1 threading模块的对象
threading模块的对象
2 thread对象数据属性
thread对象数据属性
PS: 守护线程: 明面上看不到,在后台跑的线程

3 thread对象方法
thread对象方法
3-协程
(1) 协程就是协同多任务
(2) 协程在一个进程或是一个线程中执行
(3) 不需要锁机制
(4) 对多核CPU的利用——多进程+协程

协程的实现:
使用 async 和 await 关键字实现

async 关键字 :
(1) 定义特殊函数
async def async_f():
pass
(2) 当被调用时, 不执行里面的代码, 而是返回一个协程对象
(3) 在事件循环中调度其执行前, 协程对象不执行任何操作
PS: asyncio模块:
get_event_loop() 获得事件循环队列, run_until_complete() 注册任务到队列
在事件循环中调度其执行前, 协程对象不执行任何操作 (asyncio模块用于事件循环)

await 关键字 :
(1) 等待协程完成
(2) 当遇到阻塞调用的函数的时候, 使用await方法将协程的控制权让出, 以便loop调用其他的协程

例:

import asyncio

async def do_sth(x):
    """ 定义协程函数 """
    print('等待中:{0}'.format(x))
    # 等待 x 秒
    await asyncio.sleep(5)

# 判断是否为协程函数
print(asyncio.iscoroutinefunction(do_sth))

coroutine = do_sth(5)
# 事件的循环队列
loop = asyncio.get_event_loop()
# 注册任务
task = loop.create_task(coroutine)
print(task)
# 等待协程任务执行结束
loop.run_until_complete(task)
print(task)

输出:

True
<Task pending coro=<do_sth() running at E:/PyCharmWP/test_xc1.py:4>>
等待中:5
<Task finished coro=<do_sth() done, defined at E:/PyCharmWP/test_xc1.py:4> result=None>

协程之间的数据通信
例:

import asyncio
import random

async def add(store, name):
    """ 写入数据到队列 """
    for i in range(5):
        # 往队列中添加数字
        num = '{0}-{1}'.format(name, i)
        await store.put(i)
        await asyncio.sleep(random.randint(1, 3))
        print('{2} add one...{0},size:{1}'.format(
            num, store.qsize(),name
        ))

async def reduce(store):
    """ 从队列中删除数据 """
    for i in range(10):
        rest = await store.get()
        print('reduce one...{0}, size:{1}'.format(rest, store.qsize()))

if __name__ == '__main__':
    # 准备一个队列
    store = asyncio.Queue(maxsize=5)
    a1 = add(store, 'a1')
    a2 = add(store, 'a2')
    r1 = reduce(store)
    # 添加到事件队列
    loop = asyncio.get_event_loop()
    loop.run_until_complete(asyncio.gather(a1, a2, r1))
    loop.close()
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值