from multiprocessing import Process,current_process
import os
current_process().pid # 产看当前进程号
os.getpid() # 查看当前进程号
os.getppid() # 查看当前进程的父进程号
"""
windows终端命令
tasklist
tasklist | findstr PID
mac终端命令
ps aux
ps aux|grep PID
"""
"""
僵尸进程:进程结束后不会立刻释放占用的资源(PID),会保留一段时间共父进程查看
孤儿进程:子进程存活,父进程意外死亡,孤儿进程操作系统会自动回收相应资源"""
"""
被守护进程结束之后守护进程也跟着结束
"""
# 如何开启 在start语句之前写以下代码即可
p.daemon = True
p.start()
"""
多个人在操作同一份数据的时候会出现数据错乱的问题
针对上述问题我们通常都是加锁处理
作用:
将并发变成串行,牺牲了程序的运行效率但是保证了数据的安全
注意:
只在操作数据的部分加锁即可
锁尽量不要自己去处理 很容易造成死锁现象
扩展:行锁 表锁
操作表格中的一行数据的时候其他人都不能操作
操作一张表的时候其他人都不能操作
"""
from multiprocessing import Lock
mutex = Lock()
# 枪锁
mutex.acquire()
# 释放锁
mutex.release()
# 模拟抢票
"""
队列:先进先出
堆栈:先进后出
"""
from mulitprocessing import Queue
q = Queue() # 括号内可以放数字来限制队列的大小
q.put() # 放数据 当队列满了再放 阻塞
q.get() # 取数据 当队列空了再取 堵塞
q.full() # 判断队列是否满了
q.empty() # 判断队列是都空了
q.get_nowait() # 取数据的时候如果没有数据直接报错
q.get(timeout=5) # 取数据的时候如果没有数据等5s还没有则直接报错
"""
进程之间是无法直接进行数据交互的,但是可以通过队列或者管道实现数据交互
管道:
队列:管道+锁
本地测试的时候才可能会用到Queue,实际生产用的都是别人封装好的功能非常强大的工具
redis
Kafka
RQ
"""
"""
生产者 +消息队列 + 消费者
消息队列的存在
# JoinableQueue
"""
"""
可以被等待的q
你在往队列中放数据的时候 内部有一个计数器自动加1
你在从队列中放数据的时候 调用task_done() 内部计时器自动减1
q.join() 当计数器为0的时候才继续往下运行
"""
"""
进程:资源单位
线程:执行单位
线程才是真正干活的人,干活的过程中需要的资源由线程所在的进程提供
每一个进程肯定都自带一个线程
同一个进程内可以创建多个线程
"""
"""
开进程
申请内存空间
"拷贝代码"
消耗资源较大
开线程
同一个进程内创建多个线程 无需上述两部操作,消耗资源相对较小
"""
from multiprocessing import Process
from threading import Thread
import time
def task(name):
print('%s is running' % name)
time.sleep(1)
print('%s is over' % name)
# 开启线程不需要在main下面执行代码 直接书写就可以
# 但是我们还是习惯性的将启动命令写在main下面
t = Thread(target=task, args=('tan',))
p = Process(target=task, args=('wei',))
p.start() # 创建线程的开销非常小 几乎是代码一执行线程就已经创建了
print('主')
from threading import Thread
import time
class MyThead(Thread):
def __init__(self,name):
"""针对双下划线开头并结尾(__init__)的方法 统一读成双下init"""
# 重写了别人的方法 又不知道别人的方法里有什么 就调用父类的方法
super().__init__()
self.name = name
def run(self):
print('%s is running'%self.name)
time.sleep(1)
print('tan dsb')
import socket
from threading import Thread
from multiprocessing import Process
"""
服务端:
1.要有固定的IP和PORT
2.24小时不间断提供服务
3.并发式的提供服务
"""
server = socket.socket() # 括号内不加参数默认就是TCP协议
server.bind(('192.168.1.101', 8081))
server.listen(5)
# 将服务的代码单独封装成一个函数
def talk(conn):
# 通信循环
while True:
try:
data = conn.recv(1024)
# 针对mac linux 客户端断开连接后
if len(data) == 0:
break
print(data.decode('utf-8'))
conn.send(data.upper())
except ConnectionResetError as e:
print(e)
break
conn.close()
# 连接循环
while True:
conn, addr = server.accept() # 接客
# 叫其他人服务客户
t = Thread(target=talk, args=(conn,)) # 线程
# t = Thread(target=talk, args=(conn,)) # 进程
t.start()
"""客户端"""
import socket
client = socket.socket()
client.connect(('192.168.1.101', 8081))
while True:
client.send(b'hello world')
data = client.recv(1024)
print(data.decode('utf-8'))
from threading import Thread
import time
def task(name):
print('%s is running' % name)
time.sleep(3)
print('%s is over' % name)
if __name__ == '__main__':
t = Thread(target=task, args=('tan',))
t.start()
t.join() # 主线程等待子线程运行结束再执行
print('主')
同一个进程下的多个线程数据是共享的
from threading import Thread
import time
money = 100
def task():
global money
money = 666
if __name__ == '__main__':
t = Thread(target=task)
t.start()
print(money)
线程对象属性及其他方法
from threading import Thread, active_count, current_thread
import os, time
def task(n):
# print('hello world',os.getpid())
print('hello world', current_thread().name)
time.sleep(n)
if __name__ == '__main__':
t = Thread(target=task, args=(1,))
t1 = Thread(target=task, args=(2,))
t.start()
t1.start()
t.join()
print('主', active_count()) # 统计当前正在活跃的线程数
print('主', os.getpid())
print('主', current_thread().name) # 获取线程名字
from threading import Thread
import time
def task(name):
print('%s is running' % name)
time.sleep(1)
print('%s is over' % name)
if __name__ == '__main__':
t = Thread(target=task, args=('tan',))
t.daemon = True
t.start()
print('主')
"""
主线程运行结束之后不会立刻结束 会等待所有其他非守护线程结束才会结束
因为主线程的结束意味着所在的进程结束
"""
线程互斥锁
from threading import Thread, Lock
import time
money = 100
mutex = Lock()
def task():
global money
mutex.acquire()
tmp = money
time.sleep(0.1)
money = tmp - 1
mutex.release()
if __name__ == '__main__':
t_list = []
for i in range(100):
t = Thread(target=task)
t.start()
t_list.append(t)
for t in t_list:
t.join()
print(money)
GIL全局解释器锁
ps:xiaoyuanqujing@666
"""
python解释器有多个版本
cpython
jpython
pypypython
但普遍使用的都是cpython解释器
在cpython解释器中GIL是一把互斥锁,用来阻止用一个进程下的多个线程的同时执行
同一个进程下的多个线程无法利用多核优势!
因为cpython中的内存管理不是线程安全的
内存管理(垃圾回收机制)
1.引用技术
2.标记清除
3.分代回收
"""
"""
重点:
1.GIL不是python的特点而是cpython解释器的特点
2.GIL是保证解释器级别的数据的安全
3.GIL会导致同一个进程下的多个线程无法同时执行即无法利用多核优势
4.针对不同的数据还是需要加不同的锁处理
5.解释型语言的通病:同一个进程下多个线程无法利用多核优势
"""
GIL与普通互斥锁的区别
from threading import Thread, Lock
import time
mutex = Lock()
money = 100
def task():
global money
# with mutex:
# tmp = money
# time.sleep(0.1)
# money = tmp - 1
mutex.acquire()
tmp = money
time.sleep(0.1) # 进入IO GIL自动释放
money = tmp - 1
mutex.release()
if __name__ == '__main__':
t_list = []
for i in range(100):
t = Thread(target=task)
t.start()
t_list.append(t)
for t in t_list:
t.join()
print(money)
"""
100个线程起来之后 要先去抢GIL
进入IO GIL自动释放 但是我手上还有一个自己的互斥锁
其他线程虽然抢到了GIL但是抢不到互斥锁
最终GIL还是回到你的手上 你去操作数据
"""
同一个进程下的多线程无法利用多核优势,是不是就没用了
from multiprocessing import Process
from threading import Thread
import os, time
def work():
res = 0
for i in range(100000):
res *= 0
if __name__ == '__main__':
l = []
print(os.cpu_count()) # 获取当前计算机CPU个数
start_time = time.time()
for i in range(12):
p = Process(target=work)
t = Thread(target=work)
p.start()
t.start()
l.append(p)
l.append(t)
for p in l:
p.join()
print(time.time() - start_time)
总结
"""
多进程和多线程各有优势
后面写项目通常可以多进程下面开设多线程
这样的话既可以利用多核也可以介绍资源消耗
"""