多个程序同时操作一份数据的时候很容易产生数据错乱!
为了避免数据错乱,我们要使用互斥锁
互斥锁
将并发变成串行 虽然牺牲了程序的执行效率 但是保证了数据安全
如何使用
#互斥锁 也是multiprocessing 模块中的类 我们可以在创建进程倒模块时一起倒入 from multiprocessing import Process,Lock mutex = Lock() #抢锁 mutex.acquire() #释放锁 mutex.release()
- 1
- 2
- 3
- 4
- 5
- 6
- 7
注意
互斥锁只应该出现在多个程序操作数据的地方 其他位置尽量不要加
线程是资源单位
进程相当于是车间 进程负责给颞部的线程提供相应的资源
线程是执行单位
线程相当于是车间里面的流水线 线程负责执行真正的功能
一个进程至少含有一个
多进程与多线程的区别
多进程
需要申请内存空间 需要拷贝全部代码 资源消耗大
多线程
不需要申请内存空间 也不需要拷贝全部代码 资源消耗小
同一个进程下多个线程之间资源共享
开设线程不要完整的拷贝代码 所以无论什么系统都不会出现反复操作的情况 也不需要在启动脚本执行 但是为了兼容性和统一性 习惯在启动脚本编写
线程的创建 与进程创建 代码步骤相似
方式一:
from threading import Thread
import time
#方法一
def task(name):
print(f'{name}正在运行')
time.sleep(3)
print(f'{name}运行结束')
if __name__ == '__main__':
#创建线程对象
t = Thread(target=task, args=('kk',))
t.start()
print('主线程')
方式二
import time
from threading import Thread
class My_Thread(Thread):
def __init__(self,name):
super().__init__()
self.name=name
def run(self):
print(f'{self.name} 正在运行')
time.sleep(3)
print(f'{self.name}运行结束')
obj= My_Thread('jason')
obj.start()
print('主线程')
比进程更加简单方便 消耗的资源更少
server
import socket
from threading import Thread
def get_server():
server = socket.socket()
server.bind(('127.0.0.1', 8080))
server.listen(5)
return server
def talk(sock):
while True:
data = sock.recv(1024)
print(data.decode('utf8'))
sock.send(data.upper())
if __name__ == '__main__':
server = get_server()
while True:
sock, addr = server.accept()
# 开设进程去完成数据交互
t = Thread(target=talk, args=(sock,))
t.start()
client
mport socket
client = socket.socket()
client.connect(('127.0.0.1', 8080))
for i in range(1000000):
client.send(b'hello baby')
data = client.recv(1024)
print(data.decode('utf8'))
主线程等到子线程运行结束之后再运行
from threading import Thread
import time
def task():
print('正在执行')
time.sleep(3)
print('运行结束')
t=Thread(target=task)
t.start()
t.join()
print('主线程')
from threading import Thread
money = 1000
def func():
global money
money = 888
if __name__ == '__main__':
t = Thread(target=func)
t.start()
# 确保线程运行完毕 再查找 money 结果更具有说服性
t.join()
print(money)
进程号
同一个进程下开设的多个线程拥有相同的进程号
查看线程名、线程号
from threading import Thread,current_thread
import os
#查看主线程
print(current_thread().name)
#查看线程号
print(os.getpid())
def task(name):
print(f'{name}正在运行')
#查看子线程
print(current_thread().name)
#查看子线程号
print(os.getpid())
time.sleep(3)
print(f'{name}运行结束')
主:MainThread 子:Thread-N
统计线程数
from threading import active_count()
守护线程伴随着被守护的线程的结束而结束
子线程守护主线程 伴随主线程结束 而结束
进程下所有的非守护线程结束 主线程(主进程)才能结束
from threading import Thread
import time
def task():
print('子线程运行task函数')
time.sleep(3)
print('子线程运行task结束')
t = Thread(target=task)
#t线程为守护线程
# t.daemon = True
t.start()
#不能写下面
# t.daemon = True
print('主线程')
储备知识
python解释器也是由编程语言写出来的
Cpython 用c写出来的
Jpython 用Jave写的
Pypython 用python写出来的最常用的就是C python(默认)
官方文档GIL的解释
In CPython, the global interpreter lock, or GIL, is a mutex that prevents multiple native threads from executing Python bytecodes at once. This lock is necessary mainly because CPython’s memory management is not thread-safe. (However, since the GIL exists, other features have grown to depend on the guarantees that it enforces.)
GIL的研究是Cpython解释器的的特点 不是python语言的特点
GIL本质也是一把互斥锁
GIL的存在使得同一进程下的多个线程无法同时执行(关键)
言外之意:单进程下的多线程无法利用多核优势 效率低!!
GIL的存在主要是因为Cpython解释器中垃圾回收机制不是线程安全的
误解: python的线程是垃圾 利用不到多核优势
python的线程确实无法使用多核优势 但是在IO密集型的任务下是有用的
**误解:**既然有GIL 那么以后我们写代码都不需要加互斥锁
不对 GIL只确保解释器层面数据 不会错乱(垃圾回收机制)
针对 程序中自己的数据应该自己加锁处理
所有的解释器型编程语言 都没办法做倒同一个进程下多个线程同时执行
我们平时在写代码的时候 不需要考虑GIL 只在学习和面试阶段才考虑