线程及GIL全局锁
进程在启动时,仅仅是在内存空间中申请了一块自己的空间,而线程才会被cpu执行。
线程
进程和线程都是虚拟单位,进程为资源单位,线程是执行单位
当我们创建一个进程时也会相应的创建一个线程,进程被执行时,cpu执行的其实是进程里面的线程,线程指的就是代码的执行过程,执行代码中所需要使用到的资源都向所在的进程申请
线程的特点:
共享进程内的资源(数据是共享的)
无需申请内存空间,比创建一个进程更加的节省资源
使用线程
创建线程的方式
# 方式1:
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=('egon',))
t.start() # 创建线程的开销非常小 比进程最少快几十倍
print('hello')
# 方式2:
from threading import Thread
import time
class MyThead(Thread):
def __init__(self, name):
super().__init__()
self.name = name
def run(self):
print('%s is running'%self.name)
time.sleep(1)
print('线程结束')
if __name__ == '__main__':
t = MyThead('egon')
t.start()
print('主')
线程的join方法
线程也支持join方法,用法和效果同进程的一样
t.join()
线程的属性及方法
from threading import Thread, active_count, current_thread
import os,time
def task(n):
# 线程的名字
print('hello world',current_thread().name)
time.sleep(n)
if __name__ == '__main__':
t = Thread(target=task,args=(1,))
t.daemon = True # 守护进程
t.start()
t.join()
print('hello',active_count()) # 统计当前正在活跃的线程数
print('hello',os.getpid())
print('hello',current_thread().name) # 获取线程名字
使用锁
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)
线程池
线程池的用法与进程池一样,使用的时候只需要引入即可
from concurrent.futures import ProcessPoolExecutor
线程实现TCP并发效果
import socket
from threading import Thread
server =socket.socket() # 括号内不加参数默认就是TCP协议
server.bind(('127.0.0.1',8080))
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.start()
"""客户端"""
import socket
client = socket.socket()
client.connect(('127.0.0.1',8080))
while True:
client.send(b'hello world')
data = client.recv(1024)
print(data.decode('utf-8'))
GIL全局解释器锁
在Cpython解释器中存在一把互斥锁,同一进程中,同一时间只能有一个python解释器运行。这样提高了单线程任务的执行效率,但也降低密集型运算中的效率(无法利用多核优势)。
tip:
虽然不能利用多核优势,但是线程有可能是运行在多个核心上,但是因为gil锁的存在同时只能运行一个.
存在的原因:cpython中的内存管理不是线程安全的
如果允许同时运行,那么当一个线程如果刚创建好一个变量,在没有被引用前,有可能会被GC回收掉。
多线程在GIL锁存在下的意义:
在计算密集的情况下,多线程是没有意义的,此时应该使用进程
在IO密集的情况下,多线程提高了cpu的利用率
License:
CC BY 4.0