Python笔记(二)

2016/02/15 Python 阅读次数:

多任务

附加多任务,多线程笔记

多任务是同时进行,就像歌手同时唱歌和跳舞,同步进行
windows liunx都是多任务 ,同一时刻进行多项操作
单核CPU之所以能多任务,是因为把每个程序执行时间打碎了,这种算法是时间片轮转,雨露均沾;还有一种算法,优先级调度 四核CPU就是4个单核CPU,
并发:一个CPU 使用时间片轮转的多个任务 -看起来是并发,执行程序大于核心
并行:多个CPU 每个程序占用一个单核CPU
多任务用fork
使用fork,就会创建一个进程,程序运行后就是进程,进程是有生命的
使用fork,需要先导入os模块
用变量 = os.fork()
os.for(),返回两个值
主(父)进程 生成 子进程
系统给主进程返回值>0,给子进程返回值 ==0
每次打印的顺序不一定,要看系统的调度算法

多任务多线程

threading是python程序中创建线程所需的模块
创建一个Thread类的实例对象 ---- 构造线程运行所需要的资源
thread 线程 process进程 program程序
target 目标 该参数用以指定线程在执行时的函数代码
如果想要给子线程传递参数 使用Thread类的args参数(元祖)
args是传递给线程函数的的参数元组 将该参数元祖拆包之后的结果赋值给函数形参 thd = threading.Thread(target=say_hello, args=(i,)) # say_hello(i)  

thd1 = threading.Thread(target=dance)
thd2 = threading.Thread(target=drink)
多任务 – 就是同时进行多个任务
线程是实现多任务的一种方式
一个进程里默认有一个线程,叫主线程;通过主线程创建的线程叫子线程 如果在进程中主线程创建了子线程,需要子线程结束后,主线程才会结束

查看当前进程中的线程数量  enumerate()线程列表

print(threading.enumerate())` ### 实例对象.join() 表示的意思就是 主线程 阻塞等待这个实例对象标识的线程结束
thd1.join()

多个线程对同一个数据进行读取,就会产生数据混乱
互斥锁 有我没你,有你没我

1 多任务
并行
并发
2 父线程与子线程
一个进程中默认只有一个线程 — 主线程

3 创建子线程的两种方法
使用Thread类 实例对象

class threading.Thread(group=None, target=None, name=None, args=(), kwargs={}, *, daemon=None)  

常用参数
target指定子线程运行的函数名
args执行子线程的函数代码在运行时 的参数

thd = threading.Thread()

使用Thread子类 实例对象
实现其中的run()方法

thd.start()方法 只是启动子线程的创建和执行

4 主线程等待子线程退出

默认情况下父线程会等待所有子线程退出
如果需要在子线程执行完成后 还需要执行一些代码(比如检测子线程+10000次之后的运算结果) 需要在主线程中使用 Thread实例对象.join()

5 同一个进程内部的多个线程 共享全局变量

6 多线程同步
互斥锁保证在任何一个时间点只有 一个 任务可以获取并占有 锁资源
创建锁资源 mutex = threading.Lock()

获取锁资源 mutex.require()
释放锁资源 mutex.release()
7 互斥锁的优缺点
优点
保证多任务修改共享数据时能够同步 使运算结果正确
缺点
丧失多任务优势
容易造成死锁问题

8 死锁问题

任务在获取锁时造成 一直在等待(死等) 锁资源 而不能成功获取

原因:
多种锁资源分配不当 – 银行家算法可以解决该问题
没有正确释放 –
用户规范 用完一定要关
权宜之计就是在获取锁的时候 使用非阻塞 或者 阻塞+超时等待 方式获取锁

9 acquire()方法功能说明

acquire(blocking=True, timeout=-1)   函数作用 申请锁资源   参数含义 blocking参数表示是否阻塞 True表示阻塞 False表示非阻塞(不阻塞等待)   timeoute参数表示阻塞等待的最长时间 超时则放弃等待   -1表示死等   如果是一个正数 代表等待这么秒  

返回值 表示是否成功获取到锁资源 True表示成功获取 False表示获取失败

进程

进程状态切换
就绪 除CPU资源之外的所有资源已经分配
在时间片用完的
等待 在运行的代码中有需要等待的条件(input sleep )阻塞等待

os.getpid() 获取当前进程的ID  
os.getppid() 获取当前进程的父进程PID  


pro.jion() 使用jion回收子进程的资源 在进程中,父进程是不会等待子进程结束,在线程中,父线程会等待子线程结束  

进程能间通信-Queue 对列 存储数据–类似列表
创建队列:

multiprocessing.Queue() 该参数表示队列长度   往队列中放入数据  
  
q.put(数据) 如果队列已满,则会默认阻塞等待 从队列中取出队列数据  

q.get() 如果没有数据,默认阻塞等待

队列中的长度

q.size()

在主进程中 阻塞等待子进程运行完成退出 后再继续运行 – 回收子进程的资源

pro.join()

告诉操作 直接结束子进程 而不用等待他自动退出 —- 操作系统收到后会尽快处理 因此有一定时间差

pro.terminate()

进程:一个程序可以有多个进程,是独立单位
线程:一个进程可以有多个线程,

进程创建:

.Process类的实例对象  
.start() -- 启动子进程的创建和执行  
.is_alive() -- 判断子进程的运行状态  
.terminate() -- 终止进程(不会立即子进程退出---时间差)  
.join() --- 阻塞等待子进程退出 才会继续往下执行 参数timetout表示超时等待的时间  

Process的参数
target指定子进程在运行时的函数
args 匿名参数 元组
kwargs 命名参数 字典

.pid 一定要在进程创建后再获取
.name Process-N

进程间通信-Queue队列
创建

q = multiprocessing.Queue() 参数表示队列长度 -- 数据条数   往里面放数据  

q.put(data)   取出数据  

q.get()   判断满 # 判断队列是否是满的 如果满的 返回True 否则返回false  

q.full()   判断空 # 判断队列是否为空 如果是空的返回True 否则返回False  

q.empty()   获取队列中的数据数量 mac会崩溃  

q.qsize()  

put(obj[, block[, timeout]]) obj表示数据对象   block表示是否阻塞等待 True表示阻塞等待 False表示不阻塞 不等待   timeout 表示如果等待 等待最长的时间  

get([block[, timeout]])   block表示是否阻塞等待 True表示阻塞等待 False表示不阻塞 不等待   timeout 表示如果等待 等待最长的时间  

进程线程总结
进程是操作系统进行资源
分配的基本单位
线程是操作系统进行资源
调度的基本单位

进程池:
apply 阻塞的任务添加方式 – 添加任务后 还需要等待任务执行完成 才会继续往运行
apply_async 非阻塞的任务添加方式 – 只管添加任务 不管其他

进程池的工作进程之间通信 需要使用 multiprocessing.Manager().Queue()
进程池需要Mnanger().Queueu,进行数据共享


import multiprocessing
import time
def worker1():
    """这个是一个任务"""
    for i in range(3):
    print("这是进程池中的一个任务1")
    time.sleep(1)
def worker2():
    """这个是一个任务"""
    for i in range(3):
    print("这是进程池中的一个任务2")
    time.sleep(1)
def main():
    #创建进程池
    pool = multiprocessing.Pool(3)
    #使用进程池中的进程 -- 往进程池中添加一个任务 等待任务执行完成之后 才会继续往下执行
    #func参数指定进程运行的函数代码 阻塞的任务添加方式
    pool.apply(func=worker1)
    pool.apply(func=worker2)
    # 只管添加任务 不管任务执行 -- 非阻塞
    pool.apply_async(func=worker1)
    pool.apply_async(func=worker2)
    # 关闭进程池 --- 不再接收新的任务
    pool.close()
    # 阻塞等待所有的进程任务运行结束 在join之前一定要close
    pool.join()
    print("所有任务执行完成")
if __name__ == '__main__':
    main()

迭代器

迭代 —遍历访问
可迭代对象 能用for ..in 遍历的就是可迭代对象
iterable 可迭代的意思
isinstance(mlist, Iterable) 判断是否可迭代对象 ;判断左边的对象是否可迭代类型
print(isinstance([],list))
iterable 可迭代的
iterator 迭代器
可迭代对象
的本质就是提供一个迭代器 用以迭代访问自己当中的数据

iter

迭代器的作用
通过next()操作不断获取可迭代对象中的下一个位置的数据 每一次访问完成 自动指向下一个位置

next

迭代器本身也是一个可迭代对象 所以 迭代器自己需要实现__iter__方法 返回自己self

data = [1,2,3,4,5]

instance 实例对象 — 判断左边的对象是否是可迭代类型的实例对象

 print(isinstance(mlist, Iterable)) ### 1 使用iter(可迭代对象)获取可迭代对象提供的迭代器
data_iter = iter(data)
while True: ### 2 使用next(迭代器)遍历可迭代对象中的数据 如果已经遍历完成 将抛出StopIteration
# try:
    # i = next(data_iter)
# except StopIteration as e:
    # print("迭代完成")
    # break
# else:
    # print(i)
    # print(isinstance([], list))
    # for i in mlist:
        # print(i) #### 如何判断可迭代对象 ### 迭代器的作用 for循环的实现过程

迭代实现斐波那契:


class Fbiter:
    def __init__(self,num):
        self.num = num
        self.index = 0
        self.nub1 = 0
        self.nub2 = 1
    def __iter__(self):
        return self
    def __next__(self):
        if self.index < self.num:
            self.index +=1
            data = self.nub1
            self.nub1,self.nub2 = self.nub2,self.nub1+self.nub2
            return data
        else:
            raise StopIteration
if __name__ == '__main__':
    fb = Fbiter(10)
    for i in fb:
    print(i)

生成器

生成器:
是一种吗特殊的迭代器,简单一点的生成器表达式
yield关键字的作用:
yield 暂时挂起当前函数 把 yield后面表达式的值 返回给调拥生成器的地方
恢复当前函数的执行,从yiele这句开始执行
挂起当前函数 将后面表达式的值 返回到调用生成器的地方
接收数据 并唤醒当前函数 并且紧接着上次运行的地址继续执行


import time
def worker1():
    while True:
    print("in worker1")
    yield
    time.sleep(0.5)
# F7 步进 进入函数调用 F8 步过
def worker2():
    while True:
    print("in worker2")
    yield # 挂起当前函数 切换到调用该生成器对象的地方
    time.sleep(0.5)
if __name__ == '__main__':
# 调用生成器函数 产生生成器对象
    w1 = worker1()
    w2 = worker2()
    while True:
    next(w1)
    next(w2)

唤醒生成器的两种方式:
生成器.send(data)
next(生成器) ===生成器.send(data)
在一般情况下, send 和 next 可以混用,但是第一次调拥生成器对象时,必须使用next


def fib(n):
    """带有yield关键字的函数已经不是普通函数 而是生成器函数"""
    current = 0
    num1, num2 = 0, 1
    while current < n:
        num = num1
        num1, num2 = num2, num1+num2
        current += 1
        # 两层含义 1 暂时挂起当前函数 把yield后表达式的值 返回给调用生成器的地方
        # 2 next() send()恢复当前函数的执行 从yield这条语句下一个位置开始
        yield num
        # 调用生成器函数 产生生成器对象
f = fib(10)
# 迭代访问生成器对象 进行执行
# for i in f:
# # i = next(f)
# print(i)
while True:
    try:
        num = next(f)
    except StopIteration as e:
        print("运行完成" + e.value)
        break
    else:
        print(num)

协成 —-切换

monkey.patch_all() # 破解阻塞
生成器.send(“数据”)
next(生成器) === 生成器.send(None)

在第一次调用生成器对象的是 必须使用next()
在后续的情况下 send和next可以混用
协程 – 切换
gevent提供了能够自动切换任务的功能 但是需要破解

gevent.spawn() 启动协程的创建和运行


from gevent import monkey
monkey.patch_all() # 破解哪些会阻塞任务的函数 time.sleep() recv recvfrom
import gevent
import time
def fun():
    for i in range(3):
    print(gevent.getcurrent())
    # 默认阻塞当前任务
    # 此时使用的time.sleep()函数已经被破解了 不会再阻塞当前函数了
    # 这样这个协程在等待1秒之后 就能做其他事情 自动切换 到别人执行了
    time.sleep(1)
# 创建并启动 协程
g1 = gevent.spawn(fun)
g2 = gevent.spawn(fun)
g3 = gevent.spawn(fun)
# 等待协程运行完成
# g1.join()
# g2.join()
# g3.join()
# joinall ([]) 表示阻塞等待所有的协程执行完成
gevent.joinall([g1, g2, g3])

path_all的作用就是 使一些默认会阻塞当前任务执行的函数变得不阻塞了 – 破解
建议 这一步在导入模块后第二行就使用
greenlet协成:


import greenlet
import time
def worker1():
    while True:
        print("in worker1")
        # 切换到第二个协程执行
        g2.switch()
        time.sleep(0.5)
def worker2():
    while True:
        print("in worker2")
        # 切换到第一个协程执行
        g1.switch()
        time.sleep(0.5)
# 创建两个协程对象 参数指定该协程执行的代码
g1 = greenlet.greenlet(worker1)
g2 = greenlet.greenlet(worker2)
g1.switch()

Search

    Table of Contents