进程-线程-协程

一、线程锁
互斥锁

1、互斥锁:解决资源争用,数据读取不一致等问题

import threading
import time
num = 0
def sum_num(i):
    lock.acquire()
    global num
    time.sleep(1)
    num +=i
    print(num)
    lock.release()
t_list = []

lock = threading.Lock()  #创建一个互斥锁对象
for i in range(6):
    t = threading.Thread(target=sum_num,args=(i,))
    t_list.append(t)
    t.start()
[t.join() for t in t_list]
print("end.................")


def main():
    t_list = []
    for i in range(5):
        # get_content("https://www.baidu.com")
        #创建线程
        #target  -->指定传入的方法的名字 , 要做什么
        #args  --》 指定方法需要传入的参数  元组类型
        t = threading.Thread(target = get_content, args=("https://www.baidu.com",))
        t_list.append(t)
        #默认是前台线程,主线程执行完了,等子线程执行完再退出
        # t.setDaemon(True) #设置后台线程,主线程退出子线程也退出
        t.start()  #启动线程  #--》自动执行run方法

    for t in t_list:
        #阻塞当前环境上下文,直到为t的线程执行完成
        #谁执行这个join代码,谁就是当前环境
        t.join()
        # t.join(timeout=0.3)

main()

2、原始锁与重入锁(Lock vs RLock)

# ​Lock:原始锁  --》获取锁之前不做判断,直到获取到锁位为止
# ​RLock:重入锁  --》获取锁之前先判断,如果自己有了锁,就立即返回 

import threading
lock1 = threading.Lock()
lock2 = threading.RLock()

# lock1.acquire()
# print("lock1 acquire 1")
# lock1.acquire() #同一个线程,获得原始锁之后,没有释放又去尝试获取原始锁,就会产生
lock2.acquire()
print("lock2 acquire 1")
lock2.acquire()
print("lock2 acquire 2")
lock2.release()
print("lock2 release ....1")
lock2.release()
print("lock2 release....2")

3、死锁

#1、第一种情况
# import threading
# # lock1 = threading.Lock()
# lock1 = threading.RLock()
# lock1.acquire()
# print("lock1 acquire 1")
# lock1.acquire() #同一个线程,获得原始锁之后,没有释放又去尝试获取原始锁,就会产生死锁
# print("lock1 acquire 2")
# lock1.release()
# print("lock1 release ....1")
# lock1.release()
# print("lock1 release....2")

#第2种情况
from threading import Thread,Lock
import time
class Account:
    def __init__(self,_id,balance,lock):
        self._id = _id
        self.balance = balance
        self.lock = lock  #每个账户自带一个锁,只要对balance变态,就提前锁住
    #取钱
    def withdraw(self,amount):
        self.balance -= amount
    #存钱
    def deposite(self,amount):
        self.balance += amount
    #查看余额
    def get_balance(self):
        return self.balance
Liu = Account("liuhongjie",50000,Lock())
zheng = Account("zhengzheng",10000,Lock())
#转账函数,谁的账户金额要动,需要先上锁
#from为转出账户,to为转入账户,amount为金额
def transfer(from_,to_,amount):
    #from_账户上锁
    if from_.lock.acquire():
        from_.withdraw(amount)  #from_账户减少
        time.sleep(1)
        print(f"{from_._id} 向{to_._id} 转了{amount}元")
        if to_.lock.acquire():
            to_.deposite(amount) #to_账户增加
            to_.lock.release() #to_账户加钱完毕,解锁
        from_.lock.release() #from——账户转账完毕解锁
    print(f"{from_._id} 向{to_._id} 转了{amount}元")

# transfer(Liu,zheng,5000)
# print(Liu.get_balance())
t1 = Thread(target=transfer,args=(Liu,zheng,4000)) #刘洪杰给小郑转了4000
t2 = Thread(target=transfer,args=(zheng,Liu,1000)) #小郑给刘洪杰转了1000
t1.start()
t2.start()
t1.join()
t2.join()
print(Liu.balance)
print(zheng.balance)

4、避免产生死锁:

1.尽量避免同一个线程对多个Lock进行锁定
2.多个线程需要对多个Lock进行锁定,尽量保证他们以相同的顺序加锁
3.设置超时
其他类锁
互斥锁 -- 允许一个线程执行
信号锁 -- 允许n个线程执行
事件锁 -- 条件变量  ,满足条件,全部线程都执行
条件锁 -- 信号量+事件锁,满足条件,允许n个线程访问执行

linux里的线程通信:互斥锁+信号量+条件变量
二、全局解释器锁 – GIL

1、GIL:全局解释器锁,是因为历史遗留问题存在cpython中(Jpython无此类问题)
2、作用:保证同一个进程内,同一时刻只有一个线程能执行代码,只有当io阻塞或者时间片用完时,才会释放这个GIL
3、由于GIL锁的限制,所以多线程不适合计算型任务,而更适合IO型任务,比如:网络IO(抓取网页数据)、磁盘操作(读写文件)、键盘输入
4、多进程更适合 计算密集型任务:用CPU、计算

三、操作系统

1、计算机组成:cpu+存储+I/O
2、资源:计算资源和存储资源
3、linux操作系统–》五大子系统

1.进程调度
	调度算法:
		先进先出
		短进程优先
		优先级
2.内存管理
	虚拟内存、虚拟地址映射、段页机制、缺页中断、内存的分配管理、伙伴系统
3.文件系统
	虚拟文件系统、ext系列系统、xfs系统
4.网络接口
5.进程通信
	前提:
		进程间是相互独立的
		进程就是正在运行的程序,是计算机进行资源分配的最小单位
	方式:
		管道(队列)
			匿名管道:父子进程之间才能通信
			命名管道:不是父子进程也能通信
		信号
			异步通信
			发送信号
				硬件发送
					ctrl + c
				软件发送
					kill
		信号量(与共享内存配合使用)
			相当于一把锁,可以规定同一时刻有多少程序能够访问共享内存
		共享内存
			进程间最快的通信方式
		socket
			不同主机上面的不同进程通信,也可以用在同一主机不同进程的通信
		消息队列
			支持传输的类型多一点
			内核启动就创建好了的
			存储空间比较小		

4、cpu两种状态

​	用户态(运行普通程序)
​	内核态(运行内核程序)

5、进程的组成

PCB(进程控制块,是进程的唯一标识)+数据段+代码段
是计算机正在运行的程序,是计算机资源分配的最小单位

6、线程

线程运行在进程之上,是操作系统进行调度的最小单位

7、进程和线程的关系

1、一个进程里可以有一个以上的线程,这些线程都是共享进程里的内存空间的
2、不同进程之间内存空间都是独立的
3、创建新的线程很简单,创建一个新的进程需要对其父进程进行一次克隆
4、一个线程可以控制和操作同一个进程里的其他线程,进程只能操作子进程
5、一个主线程改变,可能会影响其他线程,改变父进程不会影响子进程

8、高并发编程

多线程
多进程
多进程+多线程

9、并发和并行:

并发:交替执行(某段时间内的处理能力)
并行:同时执行

10、多进程 VS 多进程

1.一般来说多线程开销会比多进程少
2.上下文切换

11、进程的三态模型

就绪、运行、阻塞

12、linux里的五种状态:

运行R、中断S、不可中断D、僵尸Z、停止T

13、孤儿进程与僵尸进程

1.孤儿进程:
父进程退出,子进程还在运行,那么这个子进程就会成为孤儿进程,孤儿进程会被pid为1的进程所收养
2.僵尸进程:
子进程退出,父进程没有响应,父进程没有去调用wait()或者waitpid()去获取子进程的状态,子进程的进程控制块就会依然保存在系统中,这种进程称为僵尸进程
四、多线程

1、threading模块

import requests
import functools
import time
import threading

def runtime(func):
    #保留传递进来的函数的元数据,将它的元数据赋值给inner
    @functools.wraps(func)
    def inner(*args, **kwargs):  #让装饰器更加通用
        start = time.time()
        result = func(*args, **kwargs)
        end = time.time()
        print(f"函数执行花了{end -start}s")
        return result
    return inner

def get_content(url):
    text = requests.get(url).content
    time.sleep(0.5)
    print("get content")

@runtime
def main():
    t_list = []
    for i in range(5):
        # get_content("https://www.baidu.com")
        #创建线程
        #target  -->指定传入的方法的名字 , 要做什么
        #args  --》 指定方法需要传入的参数  元组类型
        t = threading.Thread(target = get_content, args=("https://www.baidu.com",))
        t_list.append(t)
        #默认是前台线程,主线程执行完了,等子线程执行完再退出
        # t.setDaemon(True) #设置后台线程,主线程退出子线程也退出
        t.start()  #启动线程  #--》自动执行run方法

    for t in t_list:
        #阻塞当前环境上下文,直到为t的线程执行完成
        #谁执行这个join代码,谁就是当前环境
        t.join()
        # t.join(timeout=0.3)
main()

2、自定义线程类创建

import requests
import threading
import time
class MyThread(threading.Thread):
    def __init__(self,num):
        super(MyThread,self).__init__()
        self.num = num
    def run(self):
        print(f"runing on numbers:{self.num}")

def get_content(url):
    text = requests.get(url).content
    time.sleep(0.5)
    print("get content")

t1 = MyThread(get_content("www.baidu.com"))
t2 = MyThread(2)
t1.start()
t2.start()
五、多进程

1、多进程-fork

import os,time
print("==================start==========")
pid = os.fork()
#父进程运行时,得到的pid为子进程的pid,子进程运行时这个pid就是0
print("outerside pid is :",pid)
if pid == 0:
    print("child process")
    time.sleep(60)
    print("child pid is:",os.getpid())
    print("child-parent pid is :",os.getppid())
else:
    print("parent process")
    time.sleep(60)
    print("parent pid is ",os.getpid())

2、多进程-multiprocessing

#各个进程都有一份独立的数据,相互隔离
from multiprocessing import Process,current_process
import time
lst = []
def task(i):
    print(current_process().name,i,'start............')
    time.sleep(2)
    lst.append(i)
    print(lst)
    print(current_process().name,i,'end...........')

if __name__ == "__main__":  # 直接运行的模块下才会相等,而不是导入模块
    p_lst = []
    for i in range(4):
        p = Process(target= task,args= (i,))
        p_lst.append(p)
        p.start()
    for p in p_lst:
        p.join()
    print("main end.......")

3、自定义进程类创建多进程

from multiprocessing import Process
import time
class Myprocess(Process):
    def __init__(self,num):
        super(Myprocess,self).__init__()
        self.num = num
    def run(self):
        print(f"runing on numbers:{self.num}")

if __name__=="__main__":
    t1 = Myprocess(1)
    t2 = Myprocess(2)
    t1.start()
    t2.start()

4、多进程数据共享

4.1、使用Manager实现数据共享,必须确保是在当前模块执行,而不是模块调用执行
from multiprocessing import Manager,Process,Lock
import time

def func(i,temp):
    with lock:
        time.sleep(1)
        temp[0] += 100
        # time.sleep(1)
        print(i,"----------------->",temp[0])

#使用manager,父进程要等待子进程结束再退出
#用socker方式实现
lock = Lock()
if __name__ == "__main__":
    manager = Manager()
    temp = manager.list([1,2,3])
    p_list = []
    for i in range(10):
        p = Process(target=func,args=(i,temp))
        p.start()
        p_list.append(p)
    for i in p_list:
        i.join() #不加jion,manager进程会先退出,子进程就访问不到manager共享的数据了
4.2、使用队列实现数据共享
from multiprocessing import Process,Queue
import time
def func(i,q):
    if not q.empty():
        print(i,"----->get value",q.get())
    time.sleep(2)
# 先进先出
if __name__ == "__main__":
    q = Queue()
    for i in range(6):
        q.put(10-i)
        p = Process(target=func,args=(i,q))
        p.start()
六、协程

1、协程拥有自己的寄存器上下文和栈,在执行函数A时,可以随时中断,去执行函数B,然后中断继续执行函数A(可以自由切换)。
2、python对协程的支持:

1.yield
    def func():
        i = 1
        while i <100:
            yield i
            i += 1

    result = func()
    print(next(result))
    print("xxxxxxxxxxxxxxxxxxxxxxxx")
    print(next(result))

2.asyncio模块
    #异步编程
    import asyncio

    async  def func1():
        print(1)
        await asyncio.sleep(2)
        print(2)

    async def func2():
        print(3)
        await asyncio.sleep(2)
        print(4)

    #创建任务列表
    tasks = [
        asyncio.ensure_future(func1()),
        asyncio.ensure_future(func2())
    ]

    #生成事件循环
    loop = asyncio.get_event_loop()
    #运行
    loop.run_until_complete(asyncio.wait(tasks))
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值