刘刚刚的个人博客

线程及GIL全局锁

创建时间:2020-09-12 11:28:55
更新时间:2020-09-12 19:57:48

进程在启动时,仅仅是在内存空间中申请了一块自己的空间,而线程才会被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锁存在下的意义:

  1. 在计算密集的情况下,多线程是没有意义的,此时应该使用进程
  2. 在IO密集的情况下,多线程提高了cpu的利用率
我的名片

昵称:shuta

职业:后台开发(python、php)

邮箱:648949076@qq.com

站点信息

建站时间: 2020/2/19
网站程序: ANTD PRO VUE + TP6.0
晋ICP备18007778号