• Day41 并发编程之线程


    Day41 并发编程之线程

    1、互斥锁

    多个程序同时操作一份数据的时候很容易产生数据错乱!
    为了避免数据错乱,我们要使用互斥锁

    互斥锁
    将并发变成串行 虽然牺牲了程序的执行效率 但是保证了数据安全

    如何使用

    #互斥锁 也是multiprocessing 模块中的类 我们可以在创建进程倒模块时一起倒入
    from multiprocessing import Process,Lock
    mutex = Lock()
    #抢锁
    mutex.acquire()
    #释放锁
    mutex.release()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    注意
    互斥锁只应该出现在多个程序操作数据的地方 其他位置尽量不要加

    2、线程

    线程是资源单位
    进程相当于是车间 进程负责给颞部的线程提供相应的资源

    线程是执行单位
    线程相当于是车间里面的流水线 线程负责执行真正的功能

    一个进程至少含有一个

    多进程与多线程的区别
    多进程
    需要申请内存空间 需要拷贝全部代码 资源消耗大

    多线程
    不需要申请内存空间 也不需要拷贝全部代码 资源消耗小

    同一个进程下多个线程之间资源共享

    2.1、创建线程的两种方式

    开设线程不要完整的拷贝代码 所以无论什么系统都不会出现反复操作的情况 也不需要在启动脚本执行 但是为了兼容性和统一性 习惯在启动脚本编写

    线程的创建 与进程创建 代码步骤相似
    方式一:

    from threading import Thread
    import time
    
    #方法一
    def task(name):
        print(f'{name}正在运行')
        time.sleep(3)
        print(f'{name}运行结束')
    
    
    if __name__ == '__main__':
        #创建线程对象
        t = Thread(target=task, args=('kk',))
        t.start()
        print('主线程')
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    方式二

    import time
    from threading import Thread
    
    
    class My_Thread(Thread):
        def __init__(self,name):
            super().__init__()
            self.name=name
    
    
        def run(self):
            print(f'{self.name} 正在运行')
            time.sleep(3)
            print(f'{self.name}运行结束')
    
    obj= My_Thread('jason')
    obj.start()
    print('主线程')
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    3、多线程实现TCP服务端并发

    比进程更加简单方便 消耗的资源更少

    server

    import socket
    from threading import Thread
    
    def get_server():
        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(data.upper())
    
    
    
    
    if __name__ == '__main__':
        server = get_server()
        while True:
            sock, addr = server.accept()
            # 开设进程去完成数据交互
            t = Thread(target=talk, args=(sock,))
            t.start()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    client

    mport socket
    
    
    client = socket.socket()
    client.connect(('127.0.0.1', 8080))
    
    
    for i in range(1000000):
        client.send(b'hello baby')
        data = client.recv(1024)
        print(data.decode('utf8'))
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    4、join方法

    主线程等到子线程运行结束之后再运行

    from threading import Thread
    import time
    
    def task():
        print('正在执行')
        time.sleep(3)
        print('运行结束')
    
    t=Thread(target=task)
    t.start()
    t.join()
    print('主线程')
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    5、同一进程下线程之间数据共享

    from threading import Thread
    
    money = 1000
    
    
    def func():
        global money
        money = 888
    
    
    if __name__ == '__main__':
        t = Thread(target=func)
        t.start()
        # 确保线程运行完毕 再查找 money 结果更具有说服性
        t.join()
        print(money)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    6、线程对象相关方法

    进程号
    同一个进程下开设的多个线程拥有相同的进程号
    查看线程名、线程号

    from threading import Thread,current_thread
    import os
    #查看主线程
    print(current_thread().name)
    #查看线程号
    print(os.getpid())
    
    def task(name):
        print(f'{name}正在运行')
        #查看子线程
        print(current_thread().name)
        #查看子线程号
        print(os.getpid())
        time.sleep(3)
        print(f'{name}运行结束')
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    :MainThread	子:Thread-N
    
    • 1

    统计线程数

    from threading import active_count()
    
    • 1

    7、守护线程

    守护线程伴随着被守护的线程的结束而结束
    子线程守护主线程 伴随主线程结束 而结束

    进程下所有的非守护线程结束 主线程(主进程)才能结束

    from threading import Thread
    import time
    
    def task():
        print('子线程运行task函数')
        time.sleep(3)
        print('子线程运行task结束')
    
    
    t = Thread(target=task)
    #t线程为守护线程
    # t.daemon = True
    t.start()
    #不能写下面
    # t.daemon = True
    print('主线程')
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    8、GIL全局解释器

    储备知识
    python解释器也是由编程语言写出来的
    Cpython 用c写出来的
    Jpython 用Jave写的
    Pypython 用python写出来的

    最常用的就是C python(默认)

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

    1. GIL的研究是Cpython解释器的的特点 不是python语言的特点

    2. GIL本质也是一把互斥锁

    3. GIL的存在使得同一进程下的多个线程无法同时执行(关键)
      言外之意:单进程下的多线程无法利用多核优势 效率低!!

    4. GIL的存在主要是因为Cpython解释器中垃圾回收机制不是线程安全的

    5. 误解: python的线程是垃圾 利用不到多核优势
      python的线程确实无法使用多核优势 但是在IO密集型的任务下是有用的

    6. **误解:**既然有GIL 那么以后我们写代码都不需要加互斥锁
      不对 GIL只确保解释器层面数据 不会错乱(垃圾回收机制)
      针对 程序中自己的数据应该自己加锁处理

    7. 所有的解释器型编程语言 都没办法做倒同一个进程下多个线程同时执行
      我们平时在写代码的时候 不需要考虑GIL 只在学习和面试阶段才考虑

  • 相关阅读:
    【数据结构】~顺序表
    pytorch基本操作:使用神经网络进行分类任务
    C++基础——指针
    视频推拉流EasyDSS平台直播通道重连无法转推的原因排查与解决
    帮助汽车制造业实现高精度脚垫上下料自动化
    1.4_7 Axure RP 9 for mac 高保真原型图 - 案例6 【旋转的唱片4】进度条_拖拽、点击
    大数据信用报告查询应该选什么样的平台?
    Maven setting.xml 配置
    Tomcat安装 + 引入依赖
    如何从缓存一致性协议MESI来理解内存屏障
  • 原文地址:https://blog.csdn.net/weixin_71967396/article/details/126275452