当前位置: 代码网 > 服务器>服务器>Linux > 互斥锁,线程技术

互斥锁,线程技术

2024年08月06日 Linux 我要评论
快来学习

互斥锁

  • 本质

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

    多个程序同时操作一份数据的时候很容易产生数据错乱!!!
    为了避免数据错乱 我们需要使用互斥锁
    
  • 模拟抢票系统(合理)

    ###  db.json 自己提前创建好
     with open('db.json', 'w', encoding='utf-8') as f:
            dic={'count':1}
           json.dump(dic, f)
        
     
    ### searc方法 打印剩余票数
    def search():
        time.sleep(random.random())
        with open('db.json', encoding='utf-8') as f:
            dic = json.load(f)
            print(f'剩余票数:{dic["count"]}')
     
     
            
    ### 模拟多用户(多进程)抢票
    def get():
        with open('db.json', encoding='utf-8') as f:
            dic = json.load(f)
        time.sleep(random.randint(0, 2))
        if dic['count'] > 0:
            dic['count'] -= 1
            with open('db.json', 'w', encoding='utf-8') as f:
                json.dump(dic, f)
            print(f'用户:{os.getpid()} ,购买成功~~')
        else:
            print(f'{os.getpid()} 没票了~~~~')
     
     
    def task(lock):
        search()        
        lock.acquire() #给抢票购买, 加锁 , 既保证了数据的安全性,也保证了数据公平性
        get()
        lock.release()# 解锁
     
     
    if __name__ == '__main__':
        lock = lock()
        for i in range(5):
            p1 = process(target=task, args=(lock,)) # 模拟5个用户进程
            p1.start()
    
  • 关键字

    acquire: 加锁,所有人去抢,抢到的人进入,其他人等待解锁
    release: 解锁,释放

  • 强调

    互斥锁只应该出现在多个程序操作数据的地方 其他位置尽量不要加
    ps:以后我们自己处理锁的情况很少 只需要知道锁的功能即可

线程理论

  • 本质

  • 进程与线程的理解

    进程是资源单位
    	进程相当于是车间 进程负责给内部的线程提供相应的资源()
    线程是执行单位
        线程相当于是车间里面的流水线 线程负责执行真正的功能
    
  • 特点

    1.线程是独立调度和分派的基本单位。
    2.同一进程中的多条线程将共享该进程中的全部系统资源。
    3.一个进程可以有很多线程,每条线程并行执行不同的任务。
    4.一个进程至少含有一个线程

  • 线程与进程的区别可以归纳为以下4点:

    1)地址空间和其它资源(如打开文件):进程间相互独立,同一进程的各线程间共享。某进程内的 线程在其它进程不可见。
    2)通信:进程间通信ipc,线程间可以直接读写进程数据段(如全局变量)来进行通信——需要进程同步和互斥手段的辅助,以保证数据的一致性。
    3)调度和切换:线程上下文切换比进程上下文切换要快得多。
    4)在多线程os中,进程不是一个可执行的实体

  • 多进程与多线程的区别

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

创建线程的两种方式

  • 强调

  • 模块导入

    from threading import thread
    
  • python创建thread对象语法如下:

    import threading
    threading.thread(target=none, name=none,  args=())
    

    主要参数说明:

    1. target 是函数名字,需要调用的函数。
    2. name 设置线程名字。
    3. args 函数需要的参数,以元祖( tuple)的形式传入
  • thread 对象主要方法说明:

    • run(): 用以表示线程活动的方法。
    • start():启动线程活动。
    • join(): 等待至线程中止。
    • isalive(): 返回线程是否活动的
    • getname(): 返回线程名。
    • setname(): 设置线程名。
  • 方法一

    from threading import thread
    import time
    
    def task(name):
        print(f'{name}正在运行')
        time.sleep(3)
        print(f'{name}运行结束')
    
    
    # t = thread(target=task, args=('jason',))
    # t.start()
    # print('主线程')
    
    if __name__ == '__main__':  # 用不用main方法都行
        t = thread(target=task, args=('jason',))
        t.start()
        print('主线程')
    
    '''
    输出结果:
    jason正在运行
    主线程
    jason运行结束
    '''
    
  • 方法二

    from threading import thread
    import time
    
    
    class mythread(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 = mythread('jason')
    obj.start()
    print('主线程')
    
    '''
    输出结果:
    jason正在运行
    主线程
    jason运行结束
    '''
    

join方法

from threading import thread
import time


def task(name):
    print(f'{name}正在运行')
    time.sleep(3)
    print(f'{name}运行结束')


t = thread(target=task, args=('jason', ))
t.start()
t.join()  # 主线程代码等待子线程代码运行完毕之后再往下执行,和进程的join方法作用一致
print('主线程')

'''
输出结果:
jason正在运行
jason运行结束
主线程
'''

同一个进程下线程间数据共享

from threading import thread

money = 1000


def func():
    global money
    money = 666


t = thread(target=func)
t.start()
t.join()  # 确保线程运行完毕 再查找money 结果更具有说服性
print(money)

'''
输出结果:
666
'''

线程对象相关方法

python的threading模块有个current_thread()函数,它将返回当前线程的示例。从当前线程的示例可以获得前运行线程名字。

from threading import thread, current_thread
current_thread().name

强调:同一个进程下开设的多个线程拥有相同的进程号

统计进程下的线程数:active_count()

守护线程

  • 本质

  • 代码实操

    from threading import thread
    import time
    
    def task():
        print('子线程运行task函数')
        time.sleep(3)
        print('子线程运行task结束')
    
    
    t = thread(target=task)
    t.daemon = true
    t.start()
    # t.daemon = true  # 会报错
    print('主线程')
    
    '''
    输出结果:
    子线程运行task函数
    主线程
    '''
    

强调:进程下所有的非守护线程结束 主线程(主进程)才能真正结束!!!

gil全局解释器锁

  • 什么是 gil 呢?

    gil 是最流程的 cpython 解释器(平常称为 python)中的一个技术术语,中文译为全局解释器锁,其本质上类似操作系统的 mutex。
    
  • gil 的功能

    在 cpython 解释器中执行的每一个 python 线程,都会先锁住自己,以阻止别的线程执行。
    
  • 储备知识

    ps:最常用的就是cpython(默认)

  • 官方文档

    翻译:

    总结:

  • 问题引出:

    1.误解:python的多线程就是垃圾 利用不到多核优势

    python的多线程确实无法使用多核优势 但是在io密集型的任务下是有用的
    

    2.误解:既然有gil 那么以后我们写代码都不需要加互斥锁

    不对 gil只确保解释器层面数据不会错乱(垃圾回收机制)
    针对程序中自己的数据应该自己加锁处理
    

总结:

以后用python就可以多进程下面开设多线程从而达到效率最大化

1.所有的解释型语言都无法做到同一个进程下多个线程利用多核优势

2.gil在实际编程中其实不用考虑

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2025  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com