avatar

刘刚刚的blog

采菊东篱下,悠然见南山🦥

  • 首页
  • 大模型应用
  • 常用软件/工具
  • Halo
  • 关于
Home 线程及GIL全局锁
文章

线程及GIL全局锁

Posted 2020-09-12 Updated 2024-12- 10
By Administrator
9~12 min read

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

python
python
License:  CC BY 4.0
Share

Further Reading

Apr 21, 2025

Typing

类型注释让python 有了更好的编辑器提示功能。 基础使用 对函数参数和返回值,进行类型注释 def surface_area_of_cube(edge_length: float) -> str:    return f"The surface area of the cube is {6 *

Feb 25, 2025

python多进程多线程下的计数及日志打印

注意点: 需要保证在多进程内的进程锁是同一个 需要保证在单进程中的多线程内线程锁是同一个 # logger.py import multiprocessing import threading ​ ​ class Logger_test:    def __init__(self, process

Dec 25, 2022

python-迭代器、生成器、协程

迭代器 Python中当容器对象提供了对迭代的支持时,可以通过container.__iter__()来返回一个迭代器对象。 迭代器需要支持以下两个方法,这两个方法共同构成了迭代器协议: iterator.__iter__() 该方法返回迭代器本身,这个方法是配合for和in使用所必须的。 iter

OLDER

常见的IO模型

NEWER

mysql数据类型

Recently Updated

  • 文本切分-语义分割(Semantic Chunking)
  • dify 并发配置优化
  • Typing
  • 大模型返回中json_schema与json_mode的区别
  • Async

Trending Tags

Halo 运维 postgresql 设计模式 linux就该这么学 nas rag odoo python 文本切分

Contents

©2025 刘刚刚的blog. Some rights reserved.

Using the Halo theme Chirpy