疫情环境下的网络学习笔记 python 4.24
4.24
昨日回顾
-
进程对象其他方法
from muliprocessing import Process,current_process import os current_process().pid # 当前进程号 os.getpid() # 当前进程号 os.getppid() # 父进程号
-
僵尸进程:进程结束之后不会立刻释放占用的资源,会八六一段时间供父进程查看
孤儿进程:父进程意外死亡,孤儿进程操作系统会自动回收相应资源
-
守护进程:在父进程结束之后立刻跟着结束
p.daemon = True p.start()
-
互斥锁
多个进程在操作同一份数据的时候可能出现数据错乱的问题,针对这个问题,通常加锁处理
将并发变成串行,牺牲程序运行效率,但是保证了数据的安全
可以从multiprocessing导入锁
from multiprocessing import Lock mutex = Lock() # 抢锁 mutex.acquire() # 进行操作 # 释放锁 mutex.release()
-
队列Queue
from multiprocessing import Queue # 可以直接使用queue,也可以在multiprocessing里导入 q = Queue(max_size) # 可以指定队列大小 q.put(1) # 放数据,队列满了再放阻塞 q.get() # 取数据,队列空了再取阻塞 q.full() q.empty() q.get_nowait() # 取不到直接报错 q.get(timeout = 5) # 等5s,取不到则报错
-
进程间通信
进程与进程之间是无法直接进行数据交互的,可以通过队列或管道实现数据交互
队列 = 管道+锁
本地测试的时候才会用到Queue,实际生产用的都是别人封装好的:redis
-
生产者与消费者模型
# 生产者+消息队列+消费者 # JoinableQueue:可以被等待的,带有计数器的queue # 在往队列中放数据的时候,计数器自动+1,从队列中取数据的时候,调用task_done方法,计数器自动-1 q.join() # 当计数器为0的时候才继续往下运行
-
线程
今日内容
- 开启线程的两种方式
- TCP服务端实现并发的效果
- 线程对象的join方法
- 线程间数据共享
- 线程对象属性及其他方法
开启进程的两种方式
threading模块
- 获得Thread对象
- 继承Thread类
# 第一种方式
from threading import Thread
import time
def task(name)
print('%s running'%name)
time.sleep(1)
print('%s over'%s)
# 开启线程不需要在main下执行代码,直接书写就可以
# 但是还是习惯性地将启动命令写在main下面
t = Thread(target=task,args=('aaa',))
t.start()
# 创建线程的开销非常小,几乎代码一执行,就创建了
# 第二种方式
from threading import Thread
import time
class MyThread(Thread):
def __init__(self,name):
super().__init__()
self.name = name
def run(self):
print('%s running'%name)
time.sleep(1)
print('%s over'%s)
if __name__ == '__main__':
t = MyThread('aaa')
t.start()
print('主线程')
TCP服务端并发
服务端的需求
- 要有固定的IP和端口
- 24小时不间断
- 能够支持并发
服务端
server = socket.socket() # 不写参数,默认是TCP
server.bind(('127.0.0.1',8080))
server.listen(5)
# 将服务的代码单独封装成一个函数
def talk(conn):
while True:
try:
data = conn.recv(1024)
if len(data == 0):break
print(data.decode('utf-8'))
except Exception as e:
print(e)
conn.close()
# 连接循环,使用线程实现并发
while True:
conn,addr = server.accept() # 自动阻塞
# t.Process(target=talk,args=(conn,))
t = Thread(target=talk,args=(conn,))
t.start()
客户端
import socket
client = socket.socket()
client.connect(('127.0.0.1',8080))
while True:
client.send(b'hello')
data = client.recv(1024)
print(data.decode('utf-8'))
补充decode,encode知识点
# 不想搞混decode和encode,可以使用str,bytes方法
# 都指定encoding为utf
data = str(data,encoding = 'utf-8')
线程对象的join方法
from threading import Thread
import time
def task(name)
print('%s running'%name)
time.sleep(1)
print('%s over'%s)
if __name__ == '__main__':
t = Thread(target=task,args=('aaa',))
t.start()
# 如果不加join,主线程在t结束之前就结束了
t.join()
# 等待t结束之后才会往下走
print('主线程')
同一进程下的线程数据共享
与进程不同
线程对象及其他方法
from threading import Thread
import time,os
def task():
print('hello',os.getpid(),current_thread().name)
# 验证线程在同一个进程下,pid是一样的
# Thread-1
if __name__ == '__main__':
t = Thread(target=task)
t.start()
print('主进程',os.getpid(),current_thread().name)
守护线程
主线程结束之后,不会立刻结束,会等待所有其他非守护子线程结束才会结束
因为主线程的结束意味着所在的进程结束
使用方法一样是在start上面加daemon为True
t = Thread(target=task)
t.daemon = True
t.start()
线程互斥锁
from threading import Thread
from multiprocessing import Lock
import time,os
money = 100
def task():
global money
mutex.acquire()
tmp = money
time.sleep(1)
money = tmp-1
mutex.release()
if __name__ == '__main__':
t_list = []
mutex = Lock()
for i inrange(100):
t = Thread(target=task)
t.start()
t_list.append(t)
for t in _list:
t.join()
print(money)
GIL全局解释器锁
- GIL不是python的特点,而是Cpython解释器的特点
- Cpython种GIL是一把互斥锁,阻止一个进程下多个线程的同时执行
- Python在设计之初就考虑到要在主循环中,同时只有一个线程在执行。虽然 Python 解释器中可以“运行”多个线程,但在任意时刻只有一个线程在解释器中运行
- 保证解释器级别的数据的安全
上下文管理mutex
with mutex:
tmp = money
time.sleep(0.1)
money = tmp - 1
# 自动抢锁,结束了自动释放锁
python同一进程下多线程是否没用
python同一进程下无法利用多核又是,是不是就没有用了
多线程是否有用要看具体情况:计算密集型任务,IO密集型任务
- 计算密集型
- IO密集型
多进程和多线程要结合使用