10.22总结

昨日回顾:

1.多道技术:

​ 单道:多个程序一串串执行。

​ 多道:切换+保存状态。

​ 1)空间上的复用:一个计算机(CPU)的空间可以提供给多个程序使用。

​ 2)时间上的复用:

​ --当前程序遇到IO操作,就会立马切换CPU执行权限。

​ --当前程序使用CPU时间过长,就会立马切换CPU的执行权限。

CPU的执行时间过长会看起来像阻塞,其实并不是阻塞,剥夺其CPU执行权限,然后会将该进程重新返回就绪态。

2.创建进程的两种方式:

from multiprocessing import Process
import time

#方式一:
def task():
    print('子进程开始执行')
    time.sleep(1)
    print('子进程结束完毕')

#在windows系统下创建,必须要在__main__执行。
__main__下:
  #target=执行任务(函数地址)
  p=Process(target=task)
  p.start() #操作系统创建子进程

  p.join()
  print('主进程')
    
    
#方式二:
-自定义类,继承Process
class MyProcess(Process):
    def run(self):
        #此处是子进程的任务
         print('子进程开始执行')
         time.sleep(1)
         print('子进程结束完毕')
        
__main__下:
   p=MyProcess()
   p.start() #操作系统创建子进程

   p.join()
   print('主进程')

3.进程对象的属性

#可以获取子进程pid号

current_process().pid---->return Process().pid

#在子进程中打印是子进程的pid号,在父进程中打印父进程的pid号。
os.getpid()
#主主进程
os.getppid()

current_process() --->p
#终止子进程
p.terminate()

#判断进程是否存活
is_alive

4.守护进程

​ 主进程结束后,子进程也要跟着结束

​ #注意:必须要在start()之前设置

p.daemon=True
p.start()
#p.daemon=True

今日内容

1.进程互斥锁

让并发变成串形,牺牲了执行效率,保证了数据安全。让程序并发执行时,需要修改数据时使用。

'''
模拟抢票软件需求:
   并发查票与抢票
   1.查看余票
   2.开始抢票
data.txt:{"ticket_num": 1}
'''
import json
import time
from multiprocessing import Process,Lock
#查看余票
def search(user):
    #打开data文件查看余票
    with open('data.txt','r',encoding='utf8') as f:
        dic=json.load(f)
    print(f'{user}查看余票,还剩{dic.get("ticket_num")}...')

#开始抢票
def buy(user):
    #先打开获取车票数据
    with open('data.txt','r',encoding='utf8') as f:
        dic=json.load(f)

    #模拟网络延时
    time.sleep(1)

    #若有票,修改data数据
    if dic.get("ticket_num") >0:
        dic['ticket_num']-=1
        with open('data.txt','w',encoding='utf8') as f:
            json.dump(dic,f)

        print(f'只有一张票了,{user}抢票成功!')

    else:
        print(f'余票为0,用户:{user}抢票失败!')

#开始抢票
def run(user,mutex):
    #并发:异步执行
    search(user)

    #串行:同步执行
    #加锁
    mutex.acquire()
    buy(user)
    #释放锁
    mutex.release()

if __name__ == '__main__':
    #调用Lock()类得到一个锁对象
     mutex=Lock()

     #同时来10个用户抢票
     for i in range(10):
         #并发开启10个子进程
         p=Process(target=run,args=(f'用户{i}',mutex))
         p.start()
        
运行结果:
用户1查看余票,还剩1...
用户0查看余票,还剩1...
用户2查看余票,还剩1...
用户3查看余票,还剩1...
用户4查看余票,还剩1...
用户5查看余票,还剩1...
用户6查看余票,还剩1...
用户8查看余票,还剩1...
用户7查看余票,还剩1...
用户9查看余票,还剩1...
只有一张票了,用户1抢票成功!
余票为0,用户:用户0抢票失败!
余票为0,用户:用户2抢票失败!
余票为0,用户:用户3抢票失败!
余票为0,用户:用户4抢票失败!
余票为0,用户:用户5抢票失败!
余票为0,用户:用户6抢票失败!
余票为0,用户:用户8抢票失败!
余票为0,用户:用户7抢票失败!
余票为0,用户:用户9抢票失败!

2.队列:

先进先出,相当于内存中产生一个队列空间,可以存放多个数据,但数据的顺序是由先进去的排在前面的。

堆栈:先进后出

from multiprocessing import Queue

#调用队列类,实例化队列对象
q=Queue(5) #若传参队列中可以存放5个数据
# q1=Queue()  #若不传参,队列中可以存放无限大的数据,前提是硬件支撑的住。

#put 添加数据,若队列中的数据满了,则卡住
q.put(1)
print('添加了1')
q.put(2)
print('添加了2')
q.put(3)
print('添加了3')
q.put(4)
print('添加了4')
q.put(5)
print('添加了5')

#查看队列是否满了
print(q.full())  #true

#添加数据,若队列满了,则会报错
# q.put_nowait(6)

#q.get():获取的数据遵循“先进先出”,若队列中无数据可取,也会卡住。
print(q.get())
print(q.get())
print(q.get())
print(q.get())
print(q.get())

#get_nowait:获取数据,队列中若没有,则会报错。
# print(q.get_nowait())  #已空-->queue.Empty

#判断队列是否为空
print(q.empty()) #True
q.put(6)
print('进入数据6')
q.put_nowait(7)
q.put_nowait(8)
q.put_nowait(9)
q.put_nowait(10)
print(q.get())
print(q.get())
print(q.get())
print(q.get())
print(q.get())
运行结果:
添加了1
添加了2
添加了3
添加了4
添加了5
True
1
2
3
4
5
True
进入数据6
6
7
8
9
10

3.IPC进程间通信:

进程间数据是互相隔离的,若想实现进程间通信,可以利用队列。

from multiprocessing import Process,Queue
def test1(q):
    data='数据1'
    q.put(data)
    print('进程1开始添加数据到队列中..')

def test2(q):
    data=q.get()
    print(f'进程2从队列中获取{data}')

if __name__ == '__main__':
    q=Queue()

    p1=Process(target=test1,args=(q, ))
    p2=Process(target=test2,args=(q, ))

    p1.start()
    p2.start()

    print('主')
    
运行结果:
主
进程1开始添加数据到队列中..
进程2从队列中获取数据数据1

4.生产者与消费者

通过队列,生产者把数据添加到队列中,消费者从队列中获取数据。

from multiprocessing import Queue,Process
import time

#生产者
def producer(name,food,q):  #生产名,食物,队列
    for i in range(9):
        data=food,i
        msg=f'{name}开始制作{data}'
        print(msg)
        q.put(data)
        time.sleep(0.1)

#消费者
def consumer(name,q):
    while True:
        data=q.get()
        if not data:
            break
        print(f'{name}开始吃{data}')

if __name__ == '__main__':
    q=Queue()

    #创造生产者
    p1=Process(target=producer,args=('tank','油条',q))
    p2 = Process(target=producer, args=('华农兄弟', '竹鼠', q))

    #创造消费者
    c1=Process(target=consumer,args=('egon',q))
    c2=Process(target=consumer, args=('jason', q))

    p1.start()
    p2.start()

    c1.daemon=True
    c2.daemon=True

    c1.start()
    c2.start()

    p2.join()
    print('主')
    
运行结果:
华农兄弟开始制作('竹鼠', 0)
egon开始吃('竹鼠', 0)
tank开始制作('油条', 0)
egon开始吃('油条', 0)
华农兄弟开始制作('竹鼠', 1)
egon开始吃('竹鼠', 1)
tank开始制作('油条', 1)
华农兄弟开始制作('竹鼠', 2)
egon开始吃('竹鼠', 2)
jason开始吃('油条', 1)
华农兄弟开始制作('竹鼠', 3)
tank开始制作('油条', 2)
egon开始吃('竹鼠', 3)
jason开始吃('油条', 2)
华农兄弟开始制作('竹鼠', 4)
tank开始制作('油条', 3)
egon开始吃('竹鼠', 4)
jason开始吃('油条', 3)
tank开始制作('油条', 4)
egon开始吃('油条', 4)
华农兄弟开始制作('竹鼠', 5)
jason开始吃('竹鼠', 5)
tank开始制作('油条', 5)
华农兄弟开始制作('竹鼠', 6)
egon开始吃('油条', 5)
jason开始吃('竹鼠', 6)
华农兄弟开始制作('竹鼠', 7)
tank开始制作('油条', 6)
egon开始吃('竹鼠', 7)
jason开始吃('油条', 6)
华农兄弟开始制作('竹鼠', 8)
egon开始吃('竹鼠', 8)
tank开始制作('油条', 7)
jason开始吃('油条', 7)
tank开始制作('油条', 8)
egon开始吃('油条', 8)
主

5.线程

--什么是线程?

线程与进程都是虚拟单位,目的是为了更好地描述某种事物。

--进程:资源单位 --线程:执行单位。

开启一个进程,一定会有一个线程,线程才是真正的执行者。

为什么要使用线程:节省内存资源。

--开启进程:

1)开辟一个名称空间,每开启一个进程都会占用一份内存资源。2)会自带一个线程。

--开启线程:

1)一个进程可以开启多个线程。2)线程的开销远小于进程。

注意:线程不能实现并行,线程只能实现并发,进程可以实现并行。

比喻:内存就像一个工厂,子进程就像一个工厂车间,线程就像车间内的流水线。

--线程之间数据是共享的。

from threading import Thread
import time

#开启线程方式1:
def task():
    print('线程开启')
    time.sleep(1)
    print('线程结束')
    
# t=Thread()
if __name__ == '__main__':
    #调用Thread线程类实例化得到线程对象
    t=Thread(target=task)
    t.start()
    
#开启线程方式2:
class MyThread(Thread):
    print('线程开启')
    time.sleep(1)
    print('线程结束')
    
t=MyThread()
t.start()

# if __name__ == '__main__':
#     t = MyThread()
#     t.start()

线程对象的属性:

from threading import Thread,current_thread
import time
#
# #开启线程方式1:
def task():
    print(f'线程开启{current_thread().name}')
    print(f'线程结束{current_thread().name}')
#
# # t=Thread()
if __name__ == '__main__':
    #调用Thread线程类实例化得到线程对象
    t=Thread(target=task)
    t.daemon=True
    t.start()
    print('主')
    
运行结果:
线程开启Thread-1
线程结束Thread-1
主

线程互斥锁

from threading import Thread
import time

'''
线程之间数据是共享的.
'''

x = 100


def task():
    print('开启线程...')
    time.sleep(1)
    global x
    x = 200
    print('关闭线程...')


# t = Thread(target=task)
# t.start()
# print(x)


if __name__ == '__main__':
    t = Thread(target=task)
    t.start()
    t.join()
    print(x)
    print('主')
    
运行结果:
开启线程...
关闭线程...
200
主
from threading import Thread, Lock
import time

mutex = Lock()

n = 100


def task(i):
    print(f'线程{i}启动...')
    global n
    mutex.acquire()
    temp = n
    time.sleep(0.1)  # 一共等待10秒
    n = temp-1
    print(n)
    mutex.release()



if __name__ == '__main__':
    t_l=[]
    for i in range(100):
        t = Thread(target=task, args=(i, ))
        t_l.append(t)
        t.start()

    for t in t_l:
        t.join()

    # 100个线程都是在100-1
    print(n)
运行结果:
线程0启动...
.
.
.
线程99启动...
99
98
97
.
.
.
3
2
1
0
0

猜你喜欢

转载自www.cnblogs.com/lidandanaa/p/11722356.html