线程、GIL全局解释器锁、进程池与线程池

多进程实现TCP服务端并发

就是将服务端的单进程一次只能服务一个客户端 进行封装成函数引进多进程
from multiprocessing import Process
import socket

def task():
    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(b'hi my name is tony')



if __name__ == '__main__':
    server = task()
    while True:
        sock, addr = server.accept()
        # 开设多进程去聊天
        P = Process(target=talk, args=(sock,))
        P.start()

互斥锁代码实操

锁:建议只加在操作数据的部分 否则整个数据程序的效率会极低
from multiprocessing import Process, Lock
import time
import json
import random

def search(name):
    with open(r'data.json', 'r', encoding= 'utf8') as f:
        data = json.load(f)
    print('%s 查看余票 目前剩余:%s' % (name, data.get('ticket_num')))
def buy(name):
    # 先查询票数
    with open(r'data.json', 'r', encoding='utf8') as f:
		data = json.load(f)
    # 模拟网络延迟
    time.sleep(random.randit(1,3))
    # 买票
    if data.get('tickle_num') > 0:
        with open(r'data.json', 'w', encoding='utf8') as f:
			data['ticket_num'] -= 1
			json.dump(data, f)
		print('%s 买票成功' % name)
      else:
		print('%s 买票失败' % name)
def ru(name, mutex):
    search(name)
    mutex.acquire()  # 抢票
    buy(name)
    mutex.release()  # 释放锁
    
if __name__ == '__main__':
    mutex = Lock()  # 产生一把锁
    for i in range(10):
        P = Process(target=run, args=('用户%s号' % i, mutex))
        P.start()
# 所有很多种 但是作用都是一样
# 如:行锁 表锁
            

线程理论

进程:进程其实是资源单位 表示一块内存空间

线程:线程才是执行单位 表示真正的代码指令

我们可以将进程比喻是车间 线程是车间里面的流水线 一个进程内部至少有一个线程

  1. 一个进程可以开设多个线程

  2. 同一个进程下的多个线程数据是共享的‘

  3. 创建进程的与现成的区别

    创建进程的消耗要远远大于线程

创建线程的两种方式

# 方式一

from threading import Thread
import time
def task(name):
	print(f'{name} is running')
    time.sleep(3)
    print(f'{name} is over')

t = Thread(target=task, args=('jason',))
t.start()
print('主线程')

'''创建线程无需考虑反复执行的问题'''

image

# 方式二

class MyThread(Thread):
	def run(self):
		print('run is running')
        time.sleep(1)
        print('run is over')
        
obj = MyThread()
obj.start()
print('主线程')
# 线程与进程之间运行时间的比较

from threading import Thread
import time
from multiprocessing import Process


def task(name):
    print(f'{name} is running')
    time.sleep(0.1)
    print(f'{name} is over')

# t = Thread(target=task, args=('jason',))
# t.start()  # 异步操作 立刻创建的线程 一瞬间
# print('主线程')

if __name__ == '__main__':
    start_time = time.time()
    # p_list = []
    # for i in range(100):
    #     p = Process(target=task, args=('用户%s' % i,))
    #     p.start()
    #     # 怎么确保每一个进程对象都创建起来 又得先让这100个进程全部运行结束
    #     p_list.append(p)  # 每次创建的进程对象添加到列表
    # for p in p_list:
    #     p.join()
    # print(time.time() - start_time)
    t_list = []
    for i in range(100):
        t = Thread(target=task, args=('用户%s' % i,))
        t.start()
        # 怎么确保每一个进程对象都创建起来 又得先让这100个进程全部运行结束
        t_list.append(t)  # 每次创建的进程对象添加到列表
    for t in t_list:
        t.join()
    print(time.time() - start_time)

image
image

多线程实现TCP服务端并发

# 服务端
from threading import Thread
import socket

s = socket.socket()
s.bind(('127.0.0.1', 8080))
s.listen(5)

def task(sock):
    while True:
        data = sock.recv(1024)
        print(data.decode('utf8'))
        sock.send('哈哈哈'.encode('utf8'))
        
if __name__ == '__main__':
    while True:
        sock, addr = s.accept()
        p = Thread(target=task, args=(sock,))
        p.start()
# 客户端
import socket

c = socket.socket()
c.connect(('127.0.0.1', 8080))

while True:
    data = input('请>>>').strip()
    if not data:
        continue
    c.send(data)
    info = c.recv(1024)
    print(info.decode('utf8'))

线程的诸多特性

1.join方法 主线程等待子线程代码运行结束再运行

2.同进程内多个线程数据共享
from threading import Thread
from multiprocessing import Process

n = 111

def work():
    global n
    n = 666
    print(n)

if __name__ == '__main__':
    p = Process(target=work)
    p.start()
    p.join()
    print('主进程', n)  # 毫无疑问子进程p已经将自己的全局的n改成了666,但改的仅仅是它自己的,查看父进程的n仍然为111

    # t = Thread(target=work)
    # t.start()
    # t.join()
    # print('主线程', n)  # 查看结果为666, 因为同一进程内的线程之间共享进程内的数据
# 同一进程内的线程共享该进程的数据
3. current_thread()  查看线程

4.active_count()  获取当前线程数量

GIL全局解释器锁

  1. 在CPython中解释器中存在全局解释器锁简称GIL

    python解释器有很多类型

    ​ CPython JPyhon PPython (常用的是CPython解释器)

  2. GIL本质也是一把互斥锁 用来阻止同一个进程内多个线程同时执行

  3. GIL的存在是因为CPython解释器中内存管理不是线程安全的(垃圾回收机制)

保证同一时间内只有一个线程在执行。线程非独立的,所以同一进程里线程是数据共享,当各个线程访问数据资源时会出现“竞争”状态,即数据可能会同时被多个线程占用,造成数据混乱,这就是线程的不安全。所以引进了互斥锁,确保某段关键代码、共享数据只能由一个线程从头到尾完整地执行
image

# 官方文档对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.

结论:在Cpython解释器中,同一个进程下开启的多线程,同一时刻只能有一个线程执行,无法利用多核优势

验证GIL的存在

from threading import Thread

num = 100

def task():
    global num
    num -= 1

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(num)

GIL与普通互斥锁

既然CPython解释器中有GIL 那么我们以后写代码是不是就不需要操作锁了!!!
"""
GIL只能够确保同进程内多线程数据不会被垃圾回收机制弄乱 
	并不能确保程序里面的数据是否安全
"""
import time
from threading import Thread,Lock

num = 100


def task(mutex):
    global num
    mutex.acquire()
    count = num
    time.sleep(0.1)
    num = count - 1
    mutex.release()


mutex = Lock()
t_list = []
for i in range(100):
    t = Thread(target=task,args=(mutex,))
    t.start()
    t_list.append(t)
for t in t_list:
    t.join()
print(num)

python多线程是否有用

需要分情况
	情况1
    	单个CPU
    	多个CPU
	情况2
    	IO密集型(代码有IO操作)
       计算密集型(代码没有IO)
 
1.单个CPU
	IO密集型
    	多进程
        申请额外的空间 消耗更多的资源
		多线程
        消耗资源相对较少 通过多道技术
      ps:多线程有优势!!!
 	计算密集型
    	多进程
        申请额外的空间 消耗更多的资源(总耗时+申请空间+拷贝代码+切换)
 		多线程
        消耗资源相对较少 通过多道技术(总耗时+切换)
      ps:多线程有优势!!!
2.多个CPU
	IO密集型
   		多进程
         总耗时(单个进程的耗时+IO+申请空间+拷贝代码)
       多线程
    	  总耗时(单个进程的耗时+IO)
       ps:多线程有优势!!!
	计算密集型
    	多进程
       	  总耗时(单个进程的耗时)
    	多线程
          总耗时(多个进程的综合)
       ps:多进程完胜!!!
 
from threading import Thread
from multiprocessing import Process
import os
import time


def work():
    # 计算密集型
    res = 1
    for i in range(1, 100000):
        res *= i


if __name__ == '__main__':
    # print(os.cpu_count())  # 12  查看当前计算机CPU个数
    start_time = time.time()
    # p_list = []
    # for i in range(12):  # 一次性创建12个进程
    #     p = Process(target=work)
    #     p.start()
    #     p_list.append(p)
    # for p in p_list:  # 确保所有的进程全部运行完毕
    #     p.join()
    t_list = []
    for i in range(12):
        t = Thread(target=work)
        t.start()
        t_list.append(t)
    for t in t_list:
        t.join()
    print('总耗时:%s' % (time.time() - start_time))  # 获取总的耗时

"""
计算密集型
    多进程:5.665567398071289
    多线程:30.233906745910645
"""

def work():
    time.sleep(2)   # 模拟纯IO操作


if __name__ == '__main__':
    start_time = time.time()
    # t_list = []
    # for i in range(100):
    #     t = Thread(target=work)
    #     t.start()
    # for t in t_list:
    #     t.join()
    p_list = []
    for i in range(100):
        p = Process(target=work)
        p.start()
    for p in p_list:
        p.join()
    print('总耗时:%s' % (time.time() - start_time))

"""
IO密集型
    多线程:0.0149583816528320
    多进程:0.6402878761291504
"""

死锁现象

acquire()
release()

from threading import Thread,Lock
import time

mutexA = Lock()  # 产生一把锁
mutexB = Lock()  # 产生一把锁


class MyThread(Thread):
    def run(self):
        self.func1()
        self.func2()

    def func1(self):
        mutexA.acquire()
        print(f'{self.name}抢到了A锁')
        mutexB.acquire()
        print(f'{self.name}抢到了B锁')
        mutexB.release()
        print(f'{self.name}释放了B锁')
        mutexA.release()
        print(f'{self.name}释放了A锁')

    def func2(self):
        mutexB.acquire()
        print(f'{self.name}抢到了B锁')
        time.sleep(1)
        mutexA.acquire()
        print(f'{self.name}抢到了A锁')
        mutexA.release()
        print(f'{self.name}释放了A锁')
        mutexB.release()
        print(f'{self.name}释放了B锁')

for i in range(10):
    obj = MyThread()
    obj.start()

信号量

在python并发编程中信号量相当于多把互斥锁(公共厕所)
	
from threading import Thread, Lock, Semaphore
import time
import random


sp = Semaphore(5)  # 一次性产生五把锁


class MyThread(Thread):
    def run(self):
        sp.acquire()
        print(self.name)
        time.sleep(random.randint(1, 3))
        sp.release()


for i in range(20):
    t = MyThread()
    t.start()

event事件

from threading import Thread, Event
import time

event = Event()  # 类似于造了一个红绿灯


def light():
    print('红灯亮着的 所有人都不能动')
    time.sleep(3)
    print('绿灯亮了 油门踩到底 给我冲!!!')
    event.set()


def car(name):
    print('%s正在等红灯' % name)
    event.wait()
    print('%s加油门 飙车了' % name)


t = Thread(target=light)
t.start()
for i in range(20):
    t = Thread(target=car, args=('熊猫PRO%s' % i,))
    t.start()

进程池与线程池

进程和线程能否无限制的创建 不可以
因为硬件的发展赶不上软件 有物理极限 如果我们在编写代码的过程中无限制的创建进程或者线程可能会导致计算机奔溃


降低了程序的执行效率 但是保证了计算机硬件的安全

线程池
提前创建好固定数量的线程供后续程序的调用 超出则等待

from concurrent.futures import ThreadPoolExecutor

pool = ThreadPoolExecutor(5)

括号内不传参数的话,会默认开设当前计算机cpu核数的5倍的线程,假设参数写5,代表池子里面固定有5个线程,不存在不断创建和销毁的过程。也就是说你是老板,你请的是5个固定的员工,而不是来来去去的临时工。避免了重复创建线程的开销。
image

image

image

image

进程池
提前创建好固定数量的进程供后续程序的调用 超出则等待

from concurrent.futures import ProcessPoolExecutor

pool = ProcessPoolExecutor(3)

进程池大体上跟线程池类似,ProcessPoolExecutor(3),括号内不填的话,会默认创建与“cpu核数”相同数量的进程,同样的,进程池中的进程是固定工,不会重复创建和销毁。

进程池和线程池在使用形式上是一样的,唯一不同的是:在Windows环境下,进程池要放在main方法里面,否则会报错
image

from concurrent.futures import ProcessPoolExecutor, ThreadPoolExecutor
import os
import time
import random
from threading import current_thread

# 1.产生含有固定数量线程的线程池
# pool = ThreadPoolExecutor(10)
pool = ProcessPoolExecutor(5)


def task(n):
    print('task is running')
    # time.sleep(random.randint(1, 3))
    # print('task is over', n, current_thread().name)
    # print('task is over', os.getpid())
    return '我是task函数的返回值'


def func(*args, **kwargs):
    print('from func')

if __name__ == '__main__':
    # 2.将任务提交给线程池即可
    for i in range(20):
        # res = pool.submit(task, 123)  # 朝线程池提交任务
        # print(res.result())  # 不能直接获取
        # pool.submit(task, 123).add_done_callback(func)

协程

"""
进程:资源单位
线程:执行单位
协程:单线程下实现并发(效率极高)
	在代码层面欺骗CPU 让CPU觉得我们的代码里面没有IO操作
	实际上IO操作被我们自己写的代码检测 一旦有 立刻让代码执行别的
	(该技术完全是程序员自己弄出来的 名字也是程序员自己起的)
		核心:自己写代码完成切换+保存状态
"""
import time
from gevent import monkey;

monkey.patch_all()  # 固定编写 用于检测所有的IO操作(猴子补丁)
from gevent import spawn


def func1():
    print('func1 running')
    time.sleep(3)
    print('func1 over')


def func2():
    print('func2 running')
    time.sleep(5)
    print('func2 over')


if __name__ == '__main__':
    start_time = time.time()
    # func1()
    # func2()
    s1 = spawn(func1)  # 检测代码 一旦有IO自动切换(执行没有io的操作 变向的等待io结束)
    s2 = spawn(func2)
    s1.join()
    s2.join()
    print(time.time() - start_time)  # 8.01237154006958   协程 5.015487432479858

协程实现并发

import socket
from gevent import monkey;monkey.patch_all()  # 固定编写 用于检测所有的IO操作(猴子补丁)
from gevent import spawn


def communication(sock):
    while True:
        data = sock.recv(1024)
        print(data.decode('utf8'))
        sock.send(data.upper())


def get_server():
    server = socket.socket()
    server.bind(('127.0.0.1', 8080))
    server.listen(5)
    while True:
        sock, addr = server.accept()  # IO操作
        spawn(communication, sock)

s1 = spawn(get_server)
s1.join()

如何不断的提升程序的运行效率
	多进程下开多线程 多线程下开协程
posted @   小福福  阅读(29)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
  1. 1 原来你也在这儿 温余福
  2. 2 世间美好和你环环扣扣 温余福
  3. 3 随风起舞 温余福
  4. 4 罪恶都市 温余福
罪恶都市 - 温余福
00:00 / 00:00
An audio error has occurred, player will skip forward in 2 seconds.

作词 : 王星

作曲 : 灰鸿啊/皮皮

编曲 : 夏日入侵企画

制作人 : 邢硕

节奏吉他 : 肯尼

主音吉他 : 张伟楠

贝斯 : 皮皮

鼓 : 海鑫

和声 : 邢硕

音效制作 : 邢硕

录音 : 邢硕/夏国兴

混音 : 于昊

特别鸣谢 : 张伟楠

这城市的车流和这地表的颤抖

像一颗石子落入地心之后泛起的温柔

暗涌

河水流过转角她的楼

被梦魇

轻声呓语唤醒身后的幼兽

失效感官焦灼只剩下

麻木愚钝无从感受

共同支撑全都瓦解

只是我们现在都

已忘记到底是

谁隐藏春秋

谁在大雨之后

把旗帜插在最高的楼

过去陈旧的还在坚守

内心已腐朽

摇摇欲坠不停退后

毁灭即拯救

夏日掠夺春秋

结局无法看透

眼看这情节开始变旧

所有的城池已失守

最终无法占有

无眠辗转

伴着人间破碎的旧梦

像繁星

退却后只剩下混沌的夜空

炙热

掩盖风声鹤唳的担忧

把所有失落无助反手推入

无尽的白昼

失效感官焦灼只剩下

麻木愚钝无从感受

共同支撑全都瓦解

只是我们现在都已经忘记到底是

谁隐藏春秋

谁在大雨之后

把旗帜插在最高的楼

过去的陈旧还在坚守

内心已腐朽

摇摇欲坠不停退后

毁灭即拯救

夏日掠夺春秋

结局无法看透

眼看这情节开始变旧

所有的城池早已失守

惶恐难以接受

缠绵往复不肯放手

最终无法占有

谁隐藏春秋

谁在大雨之后

把旗帜插在最高的楼

过去的陈旧还在坚守

内心已腐朽

摇摇欲坠不停退后

毁 灭 即 拯 救

谁掠夺春秋

谁在大雨之后

把旗帜插在最高的楼

过去的陈旧还在坚守

内心已腐朽

摇摇欲坠不停退后

毁灭即拯救

夏日掠夺春秋

结局无法看透

明知城池已失守

缠绵往复不肯放手

最终无法占有

点击右上角即可分享
微信分享提示