• 线程


    一.线程

    1.什么是线程(thread)

      线程:线程是cpu处理的单位(实体),线程存在于进程中.线程没有主次之分,是平等的.

    2.线程的两种创建方式

    from threading import Thread
    import time
    
    def f1(n):
        time.sleep(1)
        print(f"{n}号线程")
    
    def f2(n):
        print(f"{n}号线程")
    
    if __name__ == '__main__':
        t1 = Thread(target=f1,args=(1,))    # 创建线程   和创建进程一样
        t2 = Thread(target=f2,args=(2,))
    
        t1.start()
        t2.start()
        print(t1.ident)
        # t1.name = "666"       # 给线程的name属性赋值
        # print(t1.getName())   # getName() 获取对象的名字
        # print(t1.isAlive())   # 判断是否还运行
        # t1.join()
        print("线程")
    第一种创建方式
    class MyThread(Thread):     # 创建一个线程类,  和创建进程一样
    
        def __init__(self,name):
            super().__init__()
            self.name = name
    
        def run(self):
            print(f"{self.name}:hah")
    
    if __name__ == '__main__':
        t = MyThread("赏金猎人")
        t.start()
    
        print("线程")
    第二种创建方式

    3.线程中的几个方法

      start():启动线程

      join():等待该线程结束,再往下执行

      is_alive():查看线程是否还在运行

      getName():获取线程的名称

    4.线程中的几个属性

      name:线程的名称,可以自定义

      daemon:守护线程,默认为False

    二.线程是共享这一进程中的数据

      线程存在于同一个进程中,所以线程是共享同一进程里的数据,但是多个线程内部都有自己的数据栈,数据不共享.

    from threading import Thread
    import time,os
    
    num = 100
    def f1():
        time.sleep(1)
        global num
        num = 3
        print(os.getpid())
        print(num)
    
    if __name__ == '__main__':
        t = Thread(target=f1,)
        t.start()
        t.join()        # 等待线程代码执行完再往下执行
        print(os.getpid())
        print(num)
    验证线程中的数据是否共享

    三.线程与进程效率的对比

    1.线程比进程开销小,因为进程需要开辟内存,而线程是在进程中的.

    2.由于cpython解释器的原因,线程不能应用多核.

    from multiprocessing import Process,Pool
    from threading import Thread
    import time
    
    def f1():
        pass
    
    if __name__ == '__main__':
    
        t_start_time = time.time()
        t_lst = []
        for i in range(8):
            t = Thread(target=f1,)
            t.start()
            t_lst.append(t)
        [tt.join() for tt in t_lst]
    
        t_end_time = time.time()
        t_res_time = t_end_time - t_start_time
    
        p_start_time = time.time()
        p_lst = []
        for i in range(8):
            p = Process(target=f1, )
            p.start()
            t_lst.append(p)
        [pp.join() for pp in p_lst]
    
        p_end_time = time.time()
        p_res_time = p_end_time - p_start_time
    
        print("多线程:",t_res_time)
        print("多进程:",p_res_time)
    线程和进程创建的效率的对比

    四.守护线程和守护进程

    1.线程的结束方式:等所有线程都结束了主线程再结束.

      守护线程:其他非守护线程结束,守护线程也随之结束

    2.进程的结束方式:主进程代码先执行完,但是主进程需要等待所有的进程结束后(回收,否则子进程就成了僵尸进程)才结束.

      守护进程:随着主进程的结束而结束

    五.锁

    1.线程锁(互斥锁/同步锁)

      线程锁:锁定一段代码,只能有一个线程执行.保护数据安全

    from threading import Thread , Lock
    import time
    
    num = 100
    def f1(loc):
    
        loc.acquire()
        global num
        temp = num
        temp -= 1
        time.sleep(0.1)
        num = temp
        loc.release()
    
    if __name__ == '__main__':
        l = Lock()
        t_lst = []
        for i in range(10):
            t = Thread(target=f1,args=(l,))
            t.start()
            t_lst.append(t)
        [tt.join() for tt in t_lst]
    
        print(num)
    线程锁

    2.死锁现象

      现象:出现在锁嵌套的时候,两个线程互相抢对方已经获取的锁,双方互相等待,就造成了死锁现象

    from threading import Thread , Lock , RLock
    import time
    
    def f1(locA,locB):
    
        locA.acquire()
        print("f1抢到A锁")
        time.sleep(1)       # 停一秒,确保f2把B锁抢到
    
        locB.acquire()
        print("f1抢到B锁")
        locB.release()
        locA.release()
    
    def f2(locA,locB):
    
        locB.acquire()
        print("f2抢到了B锁")
        time.sleep(1)       # 停一秒,确保f1把A锁抢到
    
        locA.acquire()
        print("f2抢到了A锁")
        locA.release()
        locB.release()
    
    if __name__ == '__main__':
        locA = Lock()
        locB = Lock()
    
        t1 = Thread(target=f1,args=(locA,locB))
        t2 = Thread(target=f2,args=(locA,locB))
    
        t1.start()
        t2.start()
    死锁现象

    3.递归锁(RLock)

      递归锁:递归锁要使用同一把锁,只是锁名不一样.内部类似有一个计数器,获取锁的时候+1,释放锁的时候-1,为0的时候别的线程就能抢这把锁.能够解决死锁现象

    from threading import Thread , Lock , RLock
    import time
    
    def f1(locA,locB):
    
        locA.acquire()
        print("f1抢到A锁")
        time.sleep(1)       # 停一秒,确保f2把B锁抢到
    
        locB.acquire()
        print("f1抢到B锁")
        locB.release()
        locA.release()
    
    def f2(locA,locB):
    
        locB.acquire()
        print("f2抢到了B锁")
        time.sleep(1)       # 停一秒,确保f1把A锁抢到
    
        locA.acquire()
        print("f2抢到了A锁")
        locA.release()
        locB.release()
    
    if __name__ == '__main__':
        locA = locB = RLock()   # 必须是同一把递归锁,递归锁内部计数来操作什么时候释放锁
    
        t1 = Thread(target=f1,args=(locA,locB))
        t2 = Thread(target=f2,args=(locA,locB))
    
        t1.start()
        t2.start()
    
    """
    lockA = lockB = RLock()     创建一个递归锁,一定要同一个锁,不同变量名
    lockA.acquire()         一次内部计数器 +1
    lockA.release()         一次内部计数器 -1   为0的时候,别的线程就能抢锁了
    """
    递归锁解决死锁现象

    4.GIL(Global Interpreter Lock )全局解释器锁

      GIL锁:是在cpython解释器中的一把互斥锁.

       由于GIL锁的存在,线程不能应用多核,使得python在处理计算密集型的线程效率会很低.

      

    六.信号量

      和进程中的信号量一样

    七.事件

      和进程中的事件一样

  • 相关阅读:
    git 提交解决冲突(转载)
    impala系列: 时间函数
    impala系列: 字符串函数
    Impala系列: Impala常用的功能函数
    impala系列:impala特有的操作符
    impala系列: 同步Hive元数据和收集统计信息
    ETL脚本的版本管理方法和 SourceTree 使用
    几本不错的数据仓库和Hadoop书籍
    Kudu系列-基础
    sql parser
  • 原文地址:https://www.cnblogs.com/q767498226/p/10260668.html
Copyright © 2020-2023  润新知