ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

python学习day39笔记

2021-07-23 20:02:20  阅读:219  来源: 互联网

标签:__ 协程 day39 python 笔记 lock2 lock1 线程 print


死锁

指两个或两个以上的进程或线程在执行过程中,因争夺资源而导致的一种互相等待的现象,若无外力作用,他们都将无法推进下去
此时称系统处于死锁状态,这些永远在互相等待的进程称为死锁进程

死锁典型问题

科学家吃面问题
一桌科学家吃面,桌上有两个叉子,吃面需要两个叉子,一个科学家抢到叉子等面,一个科学家抢到面等叉子,僵持不下,这就是死锁

from threading import Thread,Lock
import time

def eat1(lock1,lock2,name):
    lock1.acquire()
    print(f'{name}抢到了叉子')
    time.sleep(1)
    lock2.acquire()
    print(f'{name}抢到了面')
    time.sleep(2)
    print('吃饱了')
    lock2.release()
    lock1.release()

def eat2(lock1,lock2,name):
    lock2.acquire()
    print(f'{name}抢到了面')
    time.sleep(1)
    lock1.acquire()
    print(f'{name}抢到了叉子')
    time.sleep(2)
    print('吃饱了')
    lock1.release()
    lock2.release()

if __name__ == '__main__':
    lock1 = Lock()
    lock2 = Lock()
    for i in ['liyang','egon','jason']:
        t1 = Thread(target=eat1,args=(lock1,lock2,i))
        t1.start()
    for i in ['zk','wmy','hjj']:
        t2 = Thread(target=eat2,args=(lock1,lock2,i))
        t2.start()

解决方法

用递归锁解决死锁问题

from threading import RLock
if __name__ == '__main__':
    lock1 = RLock()
    lock2 = lock1
    for i in ['liyang','egon','jason']:
        t1 = Thread(target=eat1,args=(lock1,lock2,i))
        t1.start()
    for i in ['zk','wmy','hjj']:
        t2 = Thread(target=eat2,args=(lock1,lock2,i))
        t2.start()
        
这个RLock内部维护着一个Lock锁和一个counter变量,counter记录了acquire的次数,如果counter不为0,则其他线程无法获得锁,从而使得线程可以连续多次require

线程队列queue

queue队列:使用 import queue ,用法与进程Queue一样

在同一个进程下多个线程数据是共享的,之所以使用队列是因为队列是管道+锁,为了保证数据的安全才使用队列

先进先出

from queue import Queue
if __name__ == '__main__':
    q = Queue()
    q.put('ly dsb')
    q.put('我最帅')
    print(q.get())
    
# ly dsb

先进后出

栈的排列方式

from queue import Queue
if __name__ == '__main__':
    q = LifoQueue()
    q.put('ly dsb')
    q.put('我最帅')
    print(q.get())
    
# 我最帅

优先级队列

from queue import PriorityQueue
if __name__ == '__main__':
    q = PriorityQueue()
    q.put((20,'ly dsb'))
    q.put((10,'我最帅'))
    # put传入一个元组,原则的第一个元素是优先级,通常是数字,也可以是非数字之间的比较,数字越小在队列内优先级越高
    print(q.get())

# (10, '我最帅')

协程

协程基础

协程在线程内,线程在进程内
协程:是单线程下的并发,又称微线程,纤程。英文名Coroutine
协程是一种用户态的轻量级线程,即协程是有用户程序自己控制调度的

强调1:python的线程属于内核级别,即由操作系统控制调度(如单线程遇到io或执行时间过长就会被迫交出cpu执行权限,切换其他线程运行)
强调2:单线程内开启协程,一旦遇到io就会从应用程序级别(而非操作系统)控制切换,以此来提升效率(非io操作的切换与效率无关)


对比操作系统控制线程的切换,用户在单线程内控制协程的切换的优缺点

优点1:协程的切换开销更小,属于程序级别的切换,操作系统完全感知不到,因而更加轻量级
优点2:单线程内就可以实现并发的效果,最大限度的利用cpu

缺点1:协程的本质是单线程下,无法利用多核,可以是一个程序开启多个进程,每个进程内开启多个线程
缺点2:协程指的是单个线程,因而一旦协程出现阻塞,将会阻塞整个线程


总结协程特点:
1.必须在只有一个单线程里实现并发
2.修改共享数据不需加锁
3.用户程序里自己保存多个控制流的上下文栈
4.附加:一个协程遇到io操作自动切换到其他协程(如何实现检测io,yield、greenlet都无法实现,就用到了gevent模块(select机制))

greenlet模块

首先安装模块:pip3 install greenlet

from greenlet import greenlet

def num1():
    print('1')
    g2.switch()
    print('2')
    
def num2():
    print('3')
    g1.switch()
    print('4')
    
g1 = greenlet(num1)
g2 = greenlet(num2)
g1.switch()

# 1
# 3
# 2

greenlet就是用来在线程之间来回切换运行,切换后暂停之前运行的任务

gevent模块

首先安装模块:pip install gevent

import gevent

def num1():
    print('1')
    # time.sleep(2)
    gevent.sleep(2)
    print('2')

def num2():
    print('3')
    # time.sleep(2)
    gevent.sleep(2)
    print('4')

g1 = gevent.spawn(num1)
g2 = gevent.spawn(num2)
g1.join()
g2.join()

# 1
# 3
# 2
# 4

gevent会自动捕捉到gevent的io操作,然后切换进程

如果非要使用time模块还要切换线程,加上猴子补丁:
from gevent import monkey
monkey.patch_all()

标签:__,协程,day39,python,笔记,lock2,lock1,线程,print
来源: https://www.cnblogs.com/Milu-Mi/p/15050314.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有