• GIL全局解释器锁


    day32-GIL全局解释器锁-1.死锁2.信号量3.Event事件4.线程queue
    0.内容回顾

     1 前日内容回顾
     2     1.IPC机制 进程间通信
     3         进程间数据是隔离的,一般情况下是无法进行数据交互的
     4         但是是可以通过进程间通信
     5         管道
     6         队列: 管道+ 7         数据只有一份,无法重复获取同一份数据
     8 
     9     2.队列:
    10     from multiprocessing import Queue
    11 
    12     q = Queue()  # 括号内支持传数字 限制的是 队列的大小
    13     # 队列先进先出
    14     # 堆栈先进后出]
    15 
    16     q.put()  # 在队列中放数据
    17     q.get()  # 从队列中取数据
    18     # 上面两个方法都会阻塞
    19 
    20     q.full()  # 判断队列是否存满
    21     q.empty()  # 判断队列是否取空
    22     q.get_nowait  # 取值,无值,不等待,直接报错
    23     # 上面三个方法不适用于多线程和多进程
    24 
    25     3.生产者消费者模型
    26         生产者:生产/制造数据
    27         消费者:消费/处理数据
    28         两者之间的的沟通介质:队列
    29 
    30         解决供需不平衡的问题:     利用队列
    31             g
    32         如何做到消费者消费完数据之后代码立即结束?
    33             1.利用join等待生产者生产完数据 再往队列中添加特定信息(None)
    34                 ps:有几个消费者,就必须有几个None
    35             2.JoinableQueue 能够被join的queue
    36                 q.task_done()  # 告诉队列数据被取出
    37                 q.join()  # 等待队列数据完全被取完
    38                 d.daemon = True 将所有的消费者设置为守护进程
    39 
    40 
    41     4.线程:
    42         进程:资源单位(车间)
    43         线程:执行单位(流水线)
    44         ps:
    45             1.每一个进程都默认自带一个"主线程"
    46             线程在运行的时候所需要和产生数据都是来源于当前线程所在的进程
    47             2.一个进程可以开设多个线程
    48             进程与线程异同
    49                 1.开进程 开销较大
    50                     申请内存空间
    51                     "拷贝"代码
    52                 2.开线程 开销较小
    53                 进程金数据是相互隔离的但是同一个进程下多个线程间数据是共享的
    54 
    55     5.创建线程的两种方式
    56         1.利用类名里传target,args参数来开设线程
    57         2.自定义类进程线程类 通过定义暴露接口run方法
    58         ps:Windows 中开设进程必须在__main__代码块内  而开线程不需要
    59 
    60     6.线程对象及其他方法:
    61         1.current_thread().name  # 当前主线程的名字
    62             MainThread
    63             Thread-1
    64             Thread-2
    65         2.active_count()   # 当前活跃的线程数
    66         ps:同一个进程中所有线程共用一个进程的端口
    67 
    68     7.join方法
    69         主线程等待当前调用join方法的线程结束之后才继续执行代码
    70         不会运行其他线程的运行
    71 
    72     8.守护线程
    73         主线程必须等待所有的非守护线程结束才能结束
    74 
    75     9.线程间互斥锁
    76         多个线程修改同一份数据会造成数据错乱的问题 所以需要加锁
    77             保证了数据的安全 但是降低了代码的运行效率 因为你将并发改为了串行
    78          ps:针对不同的数据 应该加不同的锁进行处理
    79          抢锁:    acquire()
    80          释放锁:   release()
    81 
    82     TCP服务端实现并发
    内容回顾

    2.TCP服务器实现并发

    1 import socket
    2 
    3 client = socket.socket()
    4 client.connect(('127.0.0.1',9788))
    5 while True:
    6     client.send(b'hello')
    7     date = client.recv(1024)
    8     date = date.decode('utf-8')
    9     print(date)
    client.py
     1 import socket
     2 from threading import Thread
     3 
     4 """
     5 服务端:
     6     1.要有固定IP和PORT
     7     2.24小时不间断提供服务
     8     3.能够支持并发
     9 """
    10 server = socket.socket()
    11 server.bind(('127.0.0.1',9788))
    12 server.listen(5)
    13 
    14 
    15 def task(conn):
    16     while True:
    17         try:
    18             date = conn.recv(1024)
    19             if len(date) == 0:break
    20             print(date.decode('utf-8'))
    21             conn.send(date.upper())
    22         except ConnectionResetError as e:
    23             print(e)
    24             break
    25     conn.close()
    26 
    27 
    28 # if __name__ == '__main__':
    29 while True:
    30     conn, addr = server.accept()
    31     print(addr)
    32     t = Thread(target=task, args=(conn,))
    33     t.start()
    server.py

    1.GIL全局解释器锁

     1 """"""
     2 """
     3 1.GIL--全局解释器锁
     4 官方解释:
     5 '''
     6 In CPython, the global interpreter lock, or GIL, is a mutex that prevents multiple
     7 native threads from executing Python bytecodes at once. This lock is necessary mainly
     8 because CPython’s memory management is not thread-safe. (However, since the GIL
     9 exists, other features have grown to depend on the guarantees that it enforces.)
    10 '''
    11 释义:
    12 在CPython 中,这个全局解释器锁,也称之为GIL,是一个互斥锁,防止多个线程在同一时间执行Python字节码,这个锁是非常重要的,因为CPython的内存管理非线程安全的,很多其他的特性依赖于GIL,所以即使他影响了程序效率也无法将其直接去除
    13 非线程安全:即多个线程访问同一个资源,会有问题
    14 线程安全:即多个线程访问同一个资源,不会有问题
    15 作用:CPython中有一个互斥锁,防止线程同一时间执行python代码
    16 '''
    17 """
    18 """
    19 1.ps:
    20     python解释器有很多种   最常见的就是cpython解释器
    21     GIL本质也是一把互斥锁:   将并发变成串行 牺牲效率,保证数据的安全性
    22     
    23     用来阻止同一个进程下的多个线程的同时执行
    24     (同一个进程内多个线程无法实现并行,但是可以实现并发)
    25 """
    26 """
    27 2. GIL的存在的原因?
    28     是因为CPython解释器的内存管理不是线程安全的
    29 """
    30 """
    31 3.垃圾回收机制:Python中不需要手动管理内存
    32     1.引用计数
    33     2.内存管理
    34     3.分代回收
    35 """
    36 """
    37 1.引用计数
    38     `每个数会被加上一个整型的计数器,表示这个数据被"引用"的次数
    39     例:
    40     a = 10  # 10地址次数计数为1
    41     b = a   # 计数2
    42     b = 1   # 计数1           
    43     # b = 1    b此时引用'1地址'计数为1
    44     a = 0   # 计数0   # 表示该数据已无人使用,变为"垃圾数据"
    45     # a = 0    a此时引用'0地址'计数为1
    46     
    47 2.内存管理
    48     当内存占用达到阙值,GC停止其他线程,启动垃圾清理操作(一串代码.一个线程)
    49     当垃圾回收启动后会将计数为0的数据清除掉,回收内存
    50 3.分代回收
    51 
    52     自动垃圾回收其实就是说,内部会有一个垃圾回收线程,会在某一时间运行起来,开始清理垃圾.
    53     问题:
    54         例如:线程1申请了内存,但是还没有使用,此时CPU就切换到了GC,GC将数据当成垃圾清理掉了
    55     解决方案:
    56         为了解决问题,CPython就给解释器加了互斥锁,多个线程将不可能同一时间使用解释器,保证了数据的安全性
    57     GIL加锁时机:
    58         加锁 - 只要一个线程要使用解释器就立马加锁(即:调用解释器时立即加锁)
    59         释放 - 该线程任务结束/该线程遇到IO/该线程使用解释器过长(超过设定值) 默认100纳秒
    60 
    61 """
    62 """
    63 面试:
    64 1.GIL是cpython解释器的特点,和Python无关
    65 2.单进程下多个线程无法利用,多核优势是所有解释型语言的通病
    66 3.针对不同的数据应该+不同的锁进行处理
    67 
    68 GIL用来保证线程的安全
    69 """
    70 """
    71 研究Python的多线程是否有需要分情况讨论
    72     四个任务 计算密集型的 10s
    73     单核情况下
    74         开线程更省资源
    75     多核情况下
    76         开进程 10s
    77         开线程 40s
    78     
    79     四个任务 IO密集型的
    80     单核情况下
    81         开线程更节省资源
    82     多核情况下
    83         开线程更节省资源
    84 """
    85 """
    86 python 多线程到底有没有用
    87 需要看情况再定 并且肯定是有用+的
    88 
    89 多进程+多线程配合使用 
    90 """
    1.GIL全局解释器锁.py
     1 """"""
     2 """
     3 验证 GIL 及锁的特点
     4 """
     5 from threading import Thread
     6 import time
     7 n = 100
     8 
     9 def task():
    10     global n
    11     tmp = n
    12     #time.sleep(0.1)
    13     n = tmp - 1
    14 
    15 t_list = []
    16 for i in range(100):
    17     t = Thread(target=task)
    18     t.start()
    19     t_list.append(t)
    20     #time.sleep(0.1)
    21     print(n)
    22 for p in t_list:
    23     p.join()
    24     #print(p)
    25 print(n)
    2.验证 GIL 及锁的特点.py
     1 """"""
     2 """
     3 计算机密集型:
     4     图像处理,语音处理,大数据分析
     5 """
     6 from multiprocessing import Process
     7 from threading import Thread
     8 import os
     9 import time
    10 
    11 def work():
    12     res = 0
    13     for i in range(1000):
    14 
    15         res *= i
    16 
    17         print(res)
    18 if __name__ == '__main__':
    19     l = []
    20     # 本机处理器为几核
    21     print(os.cpu_count())  # 本机为6核处理器
    22     start_time = time.time()
    23     for i in range(100):
    24         t = Process(target=work)  # 3.2s
    25         # t = Thread(target=work)  # 1.6s
    26         l.append(t)
    27         t.start()
    28 
    29     for p in l:
    30         p.join()
    31         print(p)
    32 
    33     stop_time = time.time()
    34     print(stop_time-start_time)
    3.计算机密集型.py
     1 """"""
     2 """
     3 IO密集型:
     4     input,recv,send...
     5 """
     6 from multiprocessing import Process
     7 from threading import Thread
     8 import threading
     9 import os,time
    10 
    11 def work():
    12     time.sleep(2)
    13 
    14 if __name__ == '__main__':
    15     l = []
    16     print(os.cpu_count())  # 本机为8核
    17     start = time.time()
    18     for i in range(4000):
    19         p=Process(target=work)  # range(400)耗时17.001083612442017s多,大部分时间耗费在创建进程上
    20         # p=Thread(target=work) #耗时2.051966667175293s多
    21         l.append(p)
    22         p.start()
    23     for p in l:
    24         p.join()
    25         print(p)
    26     stop = time.time()
    27     print('run time is %s' % (stop - start))
    4.IO密集型

    GIL整体认知.png


    2.死锁-了解

     1 from threading import Thread,Lock,current_thread,RLock
     2 """
     3 RLock 递归锁
     4 可以被第一个抢到锁的人连续acquire 和 release
     5 每acquire一次锁身上的计数+1
     6 每release一次锁身上的计数-1
     7 只要锁的计数不为0 其他人都不能抢
     8 
     9 """
    10 import time
    11 # mutexA = Lock()
    12 # mutexB = Lock()
    13 mutexA = mutexB = RLock()  # A,B现在是同一把锁
    14 class MyThread(Thread):
    15 
    16     # 创建线程自动触发run的方法 run方法内调用func1 func2相当于也是自动触发
    17     def run(self):
    18 
    19         self.func1()
    20         self.func2()
    21 
    22     def func1(self):
    23         mutexA.acquire()
    24         print('%s抢到A锁' % self.name)  # self.name 等价于 current_thread().name
    25         mutexB.acquire()
    26         print('%s抢到B锁' % self.name)
    27         mutexB.release()
    28         print('%s释放A锁' % self.name)
    29         mutexA.release()
    30         print('%s释放B锁' % self.name)
    31     def func2(self):
    32         mutexB.acquire()
    33         print('%s抢到B锁' % self.name)  # self.name 等价于 current_thread().name
    34         time.sleep(0.1)
    35         mutexA.acquire()
    36         print('%s抢到B锁' % self.name)
    37         mutexA.release()
    38         print('%s释放A锁' % self.name)
    39         mutexB.release()
    40         print('%s释放B锁' % self.name)
    41 
    42 for i in range(100):
    43     t = MyThread()
    44     t.start()
    45 """
    46 只要类加括号实例化
    47 无论传入的对象是否一样,结果肯定不一样
    48 (单例:是人为限制的)
    49 
    50 自己千万不要处理轻易处理锁的问题
    51 """
    52 class Demo(object):
    53     pass
    54 obj1 = Demo()
    55 obj2 = Demo()
    56 print(id(obj1),id(obj2))
    57 # 2982846821376 2982846821600
    1.死锁--递归锁.py

    3.信号量-了解

     1 """"""
     2 """信号量:可能在不同的领域中对应不同的知识点"""
     3 """
     4 信号量:    公共厕所(多个坑位)
     5 互斥锁:    一个厕所(一个坑位)
     6 """
     7 """
     8 信号量:批量处理锁的方法
     9 """
    10 from threading import Semaphore,Thread
    11 import time
    12 import random
    13 # 造一个含有3个坑位的公共厕所
    14 sm = Semaphore(3)
    15 def task(name):
    16     sm.acquire()
    17     print('%s占了一个坑位'%name)
    18     time.sleep(random.random())
    19     sm.release()
    20 start = time.time()
    21 for i in range(100):
    22     t = Thread(target=task,args=('宿友%s'%i,))
    23 
    24     t.start()
    25 stop = time.time()
    26 print('完成线程创建时间',stop-start)
    27 # 完成线程创建时间 0.018697023391723633s
    1.信号量.py
     1 from threading import Lock,Thread
     2 import time
     3 import random
     4 # 造一个含有3个坑位的公共厕所
     5 sm = Lock()
     6 def task(name):
     7     sm.acquire()
     8     print('%s占了一个坑位'%name)
     9     time.sleep(random.random())
    10     sm.release()
    11 start = time.time()
    12 for i in range(100):
    13     t = Thread(target=task,args=('宿友%s'%i,))
    14     t.start()
    15 stop = time.time()
    16 print('完成线程创建时间', stop - start)
    17 # 完成线程创建时间 0.019802570343017578
    2.对比正常锁.py

    4.Even事件

     1 """"""
     2 """
     3 join:   主等子
     4 event:   子等子
     5 
     6 """
     7 from threading import Thread,Event
     8 import time
     9 # 先生成一个event事件
    10 e = Event()  # e.set 发消息 e.wait 等待消息
    11 def light():
    12     print('红灯正亮着')
    13     time.sleep(0.1)
    14     e.set()  # 发信号
    15     print('绿灯亮了')
    16     
    17 def car(name):
    18     print('%s正在等红灯'%name)
    19     e.wait()  # 等待信号
    20     print('%s加油门飙车'%name)
    21 
    22 t = Thread(target=light)
    23 t.start()
    24 for i in range(100):
    25     t = Thread(target=car,args=('赛车手%s'%i,))
    26     t.start()
    1.even事件.py

    5.线程q
    1.线程queue.py

     1 """"""
     2 """
     3 线程queue
     4 """
     5 import queue
     6 """
     7 同一个进程下多个线程本来就是数据共享的,为什么还要用queue(队列)
     8 
     9 因为 队列 : 管道+锁
    10     使用队列  你就不需要自己手动操作锁的问题
    11     
    12 因为"锁"操作的不好 极容易产生死锁现象
    13 
    14 """
    15 '''# 1.Queue 先进先出'''
    16 q = queue.Queue(3)
    17 q.put(1)
    18 q.put(2)
    19 q.put(3)
    20 print(q.full())  # True
    21 # q.put(4)  # 阻塞
    22 print(q.get())
    23 print(q.get())
    24 print(q.get())
    25 # print(q.get())  # 阻塞
    26 print(q.empty())  # True
    27 # print(q.get_nowait())  # 取值,无值不等待,报错
    28 print("===============================================================")
    29 
    30 
    31 """# 2.LifoQueue 先进后出,其他方法与Queue一致"""
    32 t = queue.LifoQueue(3)
    33 t.put(1)
    34 t.put(2)
    35 t.put(3)
    36 print(t.full())  # True
    37 # t.put(4)  # 阻塞
    38 print(t.get())
    39 print(t.get())
    40 print(t.get())
    41 # print(t.get())  # 阻塞
    42 print(t.empty())  # True
    43 # print(t.get_nowait())  # 取值,无值不等待,报错
    44 print("===============================================================")
    45 
    46 """#  3.PriorityQueue 传入的参数要求是元组 '()' ,遵循:数字越小 优先级越高"""
    47 p = queue.PriorityQueue(3)
    48 p.put((-10,'haha'))
    49 p.put((100,'hehehe'))
    50 p.put((-5,'xxxx'))
    51 print(p.full())  # True
    52 # p.put((-10,'yyyy'))  # 阻塞
    53 print(p.get())
    54 print(p.get())
    55 print(p.get())
    56 # print(p.get())  #阻塞
    57 print(p.empty())  # True
    58 # print(p.get_nowait())  # 报错
    1.线程 Queue/LifoQueue/PriorityQueue

    readme

     1 今日内容
     2     GIL全局解释器锁
     3     Event事件
     4     信号量
     5     死锁
     6     递归锁
     7     线程queue
     8 
     9 视频:
    10 01 前日内容回顾.mp4
    11 02 TCP服务端实现并发.mp4
    12 03 GIL全局解释器锁.mp4
    13 04 验证GIL及锁的特点.mp4
    14 05 验证python多线程是否有用.mp4
    15 06 死锁现象.mp4
    16 07 递归锁.mp4
    17 08 信号量.mp4
    18 09 event事件.mp4
    19 10 线程q.mp4
    readme
  • 相关阅读:
    ext表格范例
    基于对象的EXT组件间通信
    hibernate自定义生成主健
    Amcharts
    ExtJS之面向对象编程基本知识
    在Ext里写大应用 (翻译:米米饭)
    EXT表单常用验证
    JPA 复合主键
    PowerDesign15常用技巧
    spring security和EXT
  • 原文地址:https://www.cnblogs.com/llx--20190411/p/11354626.html
Copyright © 2020-2023  润新知