并发编程3 互斥锁 线程

100 阅读3分钟

互斥锁

我们使用互斥锁的目的是为了,避免多个程序操作一份数据是发生错乱
  • 互斥锁会将并发变为串行
    • 虽然牺牲了程序的执行效率但是保证了数据安全
  • 互斥锁只出现在多个程序操作数据的地方
    • 其他位置尽量避免添加
  • 我们只需要了解互斥锁的功能即可
行锁          锁表格一行
表锁          锁表格
乐观锁        执行乐观,不上锁
悲观锁        执行悲观,执行就上锁

线程理论

  • 线程与进程的区别
    • 进程是资源单位
      • 进程负责给内部线程提供相应的资源 (车间)
    • 线程是执行单位
      • 线程负责执行真正的功能 (流水线)
    • 线程是执行单位

创建线程的两种方式

第一种:
    from threading import Thread
    from multiprocessing import Process
    import time

    def task(name):
        print(f'{name}正在运行')
        time.sleep(3)
        print(f'{name}运行结束')
    if __name__ == '__main__':
        t = Thread(target=task, args=('tom',))
        t.start()
        print('主线程')

第二种:
    from threading import Thread
    from multiprocessing import Process
    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('tom')
    obj.start()
    print('主线程')

线程相关的方法

join方法

  • 主线程等到子线程运行结束之后再运行
    例:
         from threading import Thread
         import time
    
         def task():
             print('正在执行')
             time.sleep(3)
             print('运行结束')
    
    
         t = Thread(target=task)
         t.start()
         t.join()
         print('主线程')
     # 注意join添加的位置
    

同进程下的线程间数据共享

from threading import Thread

money = 1000
def func():
    global money
    money = 666

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

线程对象相关的方法

  • 1.进程号
    • 同一个进程下开设的多个线程拥有相同的进程号
  • 2.线程名
    from threading import Thread,current_thread
    
    current_thread().name   # 查看线程名
    
      主进程的标识:MainThread
      子进程的标识:Thread-N     (随机的编号)
    
  • 查看进程下的线程数
    • active_count()

守护线程

  • 守护线程伴随着被守护的线程的结束而结束
    • 进程下的所有的非守护线程结束,主进程才能真正的结束
      from threading import Thread
      import time
      
      def task():
          print('子线程运行task函数')
          time.sleep(3)
          print('子线程运行task结束')
      
      t = Thread(target=task)
      t.start()
      print('主线程')
      
      

GIL全局解释器锁

对于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语言的特点
       1.python解释器也是由编程语言写出来的
            Cpython  用C写出来的
            Jpython  用Java写出来的
            Pypython 用python写出来的
      
    • 2.GIL本质也是一把互斥锁
    • 3.GIL的存在使得同一个进程下的多个线程无法同时执行(关键)
        单进程下的多线程无法利用多核优势 效率低!
      
    • 4.GIL的存在主要是因为cpython解释器中垃圾回收机制不是线程安全的
  • 对与多线程与互斥锁地误解
    1.误解:python的多线程就是垃圾 利用不到多核优势
       python的多线程确实无法使用多核优势 但是在IO密集型的任务下是有用的
    2.误解:既然有GIL 那么以后我们写代码都不需要加互斥锁
       GIL只确保解释器层面数据不会错乱(垃圾回收机制)
       针对程序中自己的数据应该自己加锁处理
    3.所有的解释型编程语言都没办法做到同一个进程下多个线程同时执行