Python学习21--线程和进程

因为全局解释器锁的存在,在python里经常使用多进程来代替多线程。
可以避免受GIL的限制

一、进程的创建

进程创建有如下三种方式:
1、通过multiprocessing下Process类创建子进程;
2、继承Process类,重写run方法
3、进程池

需要注意的是:进程一定要写在“主程序”中,因为windows系统在创建子进程的时候,会将当前的模块导入,如直接写在主程序中,会报错,如下所示:

import multiprocessing
def m():
    pass

p1 = multiprocessing.Process(target=m)
p1.start()

报错:

RuntimeError: 
        An attempt has been made to start a new process before the
        current process has finished its bootstrapping phase.

正确执行方法为:

import multiprocessing,os
def m():
    print("子进程执行")
    print("子进程执行时ID是{},此时子进程中父进程ID是{}".format(os.getpid(),os.getppid()))
if __name__ == "__main__":
    p1 = multiprocessing.Process(target=m)
    p1.start()
    print("父进程执行时ID是{},此时父进程中的父进程ID是{}".format(os.getpid(),os.getppid()))

输出:

父进程执行时ID是4600,此时父进程中的父进程ID是15456
子进程执行
子进程执行时ID是10292,此时子进程中父进程ID是4600

二、进程的相关操作

os.getpid():获得当前进程
os.getppid():获得当前进程的父进程
os.fork:复制进程(在windows系统下不支持)

其他方法:run、start、is_alive、name、daemon

三、进程队列

两个进程之间的区域不共享,所以进程不存在进程不安全的问题;
但是进程队列需要解决资源的通信问题;
pickle序列化解决进程之间的资源通信(同步)
在向一个进程中修改队列的时候,同时向一个进程使用pickle 序列化的方式同步队列的信息
在另外一个进程中将序列化的信息再恢复成对象

四、线程的通知和等待

wait:等待并释放当前线程占用的锁
notify:任选一个等待线程
notifyall:通知所有等待的线程
具体用法可通过如下案例实现。
需求:有生产者和消费者两个对象,仓库只能最多容纳3个产品,生产者生产大于3时不在生产,消费者消费到0后不再消费。
代码实现如下:

import threading,time
lock = threading.Condition()
def produce(li):
    for i in range(10):
        try:
            lock.acquire()
            if len(li)==3:
                print("仓库已满,生产阻塞")
                lock.wait()

            else:
                li.append("goods")
                print("{}生产了{}".format(i,li[-1]))
                lock.notify_all()
        finally:
            lock.release()

def consume(li):
    for i in range(10):
        try:
            lock.acquire()
            if len(li)==0:
                print("仓库为0,消费阻塞")
                lock.wait()
            else:
                print("{}消费了{}".format(i,li[-1]))
                li.pop(0)
                lock.notify_all()
        finally:
            lock.release()

if __name__ =="__main__":
    li=[]
    t1 = threading.Thread(target=produce,args=(li,))
    t2 = threading.Thread(target=consume,args=(li,))
    t1.start()
    t2.start()

输出:

0生产了goods
1生产了goods
2生产了goods
仓库已满,生产阻塞
0消费了goods
1消费了goods
2消费了goods
仓库为0,消费阻塞
4生产了goods
5生产了goods
6生产了goods
仓库已满,生产阻塞
4消费了goods
5消费了goods
6消费了goods
仓库为0,消费阻塞
8生产了goods
9生产了goods
8消费了goods
9消费了goods

五、线程队列

队列内部实现了锁机制。开发时候,并发操作都是会用队列完成。
使用queue Queue来创建队列的对象
有如下几种队列:
1、先进先出队列:queue.Queue()
2、先进后出(堆栈):queue.LifoQueue()
3、优先队列:queue.PriorityQueue()
先进先出队列:
queue.Queue(maxsize):maxsize指定队列的最大容量,如果maxsize不写,代表无限容量
q=queue.Queue()
q.qsize()表示队列中元素的个数
q.empty()判断队列是否为空:空为Ture,否则为False
q.full()判断队列是否为满
q.put():向队列中放元素
block阻塞状态:默认值是True 当队列已满,继续put,会使得程序阻塞
timeout:指定等待阻塞的时间, 必须block=True才有效
q.put_nowait():当向队列中put元素,如果队列已满,不等待,相当于执行 q.put(“str”,block=False)
q.get():从队列中取元素,get之后,元素就没有了
block阻塞状态:默认是True,当队列已空,继续get,则会使得程序阻塞,timeout:指定阻塞的时间,必须block=True才有效
q.get_nowait():当向队列中get元素,如果队列已满,不等待,相当于执行 q.get(“str”,block=False)
相关应用如下:

import queue
q = queue.Queue()
q.put("a")
q.put("b")
q.put("c")
print(q.empty(),q.full(),q.qsize())
print(q.get(block=True,timeout=1))
print(q.get_nowait())

输出:

False False 3
a
b

先进后出队列(堆栈):queue.LifoQueue()
相关方法与先进先出队列一样

import queue
q = queue.LifoQueue()
q.put("a")
q.put("b")
q.put("c")
while not q.empty():
    print(q.get())

输出:

c
b
a

优先队列
根据元素的优先原则排列,出队列

import queue
q = queue.PriorityQueue()
q.put("3a")
q.put("1b")
q.put("2c")
while not q.empty():
    print(q.get())

输出:

1b
2c
3a

队列内部实现了锁机制,队列是线程安全的,可以多个线程共享变量
可用队列来实现刚刚生产者消费者的案例

import queue,threading
def produce(q,name):
    i = 0
    while True:
        q.put(i)
        print("{}生产{},仓库还有{}".format(name,i,q.qsize()))
        i+=1

def consume(q,name):
    while True:
        print("{}消费{},目前还有{}".format(name,q.get(),q.qsize()))

if __name__ == "__main__":
    q = queue.Queue(3)
    t1 = threading.Thread(target=produce,args=(q,"zhangsan"))
    t2 = threading.Thread(target=consume, args=(q,"lisi"))
    t3 = threading.Thread(target=consume, args=(q,"wangwu"))
    t1.start()
    t2.start()
    t3.start()

输出结果片段如下:

zhangsan生产1779,仓库还有3
lisi消费1777,目前还有2
wangwu消费1778,目前还有1
wangwu消费1779,目前还有0
zhangsan生产1780,仓库还有1
lisi消费1780,目前还有0
zhangsan生产1781,仓库还有1

使用进程实现:

import multiprocessing,time,random,os
def produce(q):
    li = ["苹果","香蕉","梨"]
    while True:
        a = random.choice(li)
        q.put(a)
        print("进程{}生产了{}".format(os.getpid(),a))
        time.sleep(0.1)

def comsue(q):
    while True:
        print("进程{}消费了{}".format(os.getpid(),q.get()))
        time.sleep(0.2)


if __name__ == "__main__":
    q = multiprocessing.Queue(5)
    m1 = multiprocessing.Process(target=produce,args=(q,))
    m2 = multiprocessing.Process(target=comsue, args=(q,))
    m1.start()
    m2.start()

输出:

进程2568生产了梨
进程18100消费了梨
进程2568生产了香蕉
进程2568生产了梨
进程18100消费了香蕉
进程2568生产了苹果
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值