一、互斥锁
1.原因:多个程序同时操作一份数据的时候很容易产生数据错乱,为了避免数据错乱 我们需要使用互斥锁
2.作用:将并发换成串行,虽然牺牲了程序的运行效率,但是保证了数据的安全性
from multiprocessing import Process, Lock
import json
import time
import random
# 查询票数
def check_tickets(name):
# 打开文件
with open('ticket', 'r', encoding='utf8') as f:
data = json.load(f)
print(f"{name}查看剩余票数{data.get('number_of_train_tickets')}")
# 抢票
def ticket_grabbing(name):
# 查票
with open('ticket', 'r', encoding='utf8') as f:
data = json.load(f)
# 网络延迟
time.sleep(random.randint(1, 5))
# 判断是否有票
if data.get('number_of_train_tickets') > 0:
data['number_of_train_tickets'] -= 1
# 将剩余票数写入文件中
with open('ticket', 'w', encoding='utf8') as f:
json.dump(data, f)
print(f'{name}抢票成功')
else:
print(f'{name}没票啦')
def run(lock,name):
check_tickets(name)
lock.acquire()
ticket_grabbing(name)
lock.release()
if __name__ == '__main__':
lock = Lock()
for i in range(5):
p = Process(target=run, args=(lock, f'用户{i}',))
p.start()
二、线程理论
1.什么是线程:在操作系统中,每个进程都有一个内存空间,且默认有一个控制线程,CPU真正工作的是线程,如果把操作系统比作一个工厂的话,进程就是工厂里的操作间,线程就是车间里的流水线
2.进程与线程的区别
2.1 过程的区别:
进程:资源的申请与销毁,是资源的整合
线程:代码的执行
2.2内存空间的区别
进程:内存空间彼此隔离
线程:同一进程下线程之间数据共享
2.3创建速度
进程:速度较慢,耗费资源
线程:只是告诉操作系统指令,速度快
三、创建线程的两种方式
1.方式一:函数
from threading import Thread
import time
def task():
print('子线程运行开始')
time.sleep(3)
print('子线程运行结束')
thread = Thread(target=task))
thread.start()
print('主线程')
运行结果:子线程运行开始
主线程
子线程运行结束
2.方式二:类
from threading import Thread
import time
class MyThread(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 = MyThread('jason')
obj.start()
print('主线程')
四、多线程实现TPC服务端
多线程比多进程更加的方便,资源消耗少
import socket
from threading import Thread
server = socket.socket()
server.bind(('127.0.0.1', 8090))
server.listen(5)
def task(sock):
while True:
data = sock.recv(1024)
print(data.decode('utf8'))
sock.send(data.upper())
while True:
sock, address = server.accept()
thread = Thread(target=task, args=('sock',))
thread.start()
五、线程join方法
join方法:进行阻塞,使子线程运行结束后执行主线程
from threading import Thread
import time
def task():
print('子线程运行开始') # 1
time.sleep(3)
print('子线程运行结束') # 2
thread = Thread(target=task)
thread.start()
thread.join()
print('主线程') # 3
运行结果:子线程运行开始
子线程运行结束
主线程
六、同进程下多线程数据共享
资源共享:同一进程下的线程共享同一内存空间,共享这块空间内的所有资源
from threading import Thread
age = 18
def task():
global age
age = 22
print(age) # 22
thread = Thread(target=task)
thread.start()
print(age) # 22
七、线程对象相关方法
1.线程号:同一个进程号中的线程号等于进程号
2.线程名:主线程-MainThread、子线程-Thread-1
3.进程下的线程数-active_count()
from threading import Thread, current_thread, active_count
import os
import time
def task():
print(f'线程运行开始')
time.sleep(3)
print(f'线程运行结束')
print(os.getpid()) # 13452
print(current_thread().name) # Thread-1
print(active_count()) # 2
thread = Thread(target=task)
thread.start()
print('主线程') # 2
print(os.getpid()) # 13452
print(current_thread().name) # MainThread
print(active_count())
八、守护线程
进程下的所有非守护线程结束,主进程(主线程)才是真正的结束
def task():
print('子线程运行开始')
time.sleep(3)
print('子线程运行结束')
thread = Thread(target=task)
thread.daemon = True
thread.start()
print('主线程')
九、GIL简介
储备知识
1.python解释器也是由编程语言写出来的
Cpython 用C写出来的
;Jpython 用Java写出来的
Pypython 用python写出来的
ps:最常用的就是Cpython(默认)
# 官方文档对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.)
"""
1.GIL的研究是Cpython解释器的特点 不是python语言的特点
2.GIL本质也是一把互斥锁
3.GIL的存在使得同一个进程下的多个线程无法同时执行(关键)
言外之意:单进程下的多线程无法利用多核优势 效率低!!!
4.GIL的存在主要是因为cpython解释器中垃圾回收机制不是线程安全的
"""
3、4解读
1.误解:python的多线程就是垃圾 利用不到多核优势
python的多线程确实无法使用多核优势 但是在IO密集型的任务下是有用的
2.误解:既然有GIL 那么以后我们写代码都不需要加互斥锁
不对 GIL只确保解释器层面数据不会错乱(垃圾回收机制)
针对程序中自己的数据应该自己加锁处理
3.所有的解释型编程语言都没办法做到同一个进程下多个线程同时执行
ps:我们平时在写代码的时候 不需要考虑GIL 只在学习和面试阶段才考虑!!!
posted on
浙公网安备 33010602011771号