zoukankan      html  css  js  c++  java
  • 002---线程

    线程

    什么是线程

    线程: 线程是操作系统调度的最小单位,它包含在进程中。

    比喻:一条流水线工作的流程,一条流水线必须属于一个车间,一个车间的工作过程是一个进程,车间负责把资源整合到一起,是一个资源单位,而一个车间内至少有一个流水线,流水线的工作需要电源,电源就相当于cpu

    线程与进程的区别

    创建一个进程,就是创建一个车间,涉及到申请空间,而且在该空间内建至少一条流水线,但创建线程,就只是在一个车间内造一条流水线,无需申请空间,所以创建开销小。

    • 同一个进程内的多个线程共享该进程内的地址资源
    • 创建线程的开销要远小于创建进程的开销
    • 线程的开启速度更快

    开启线程的两种方式

    • 普通方式

      from threading import Thread
      
      
      def func(*args, **kwargs):
          print(args, kwargs)
          print('子线程启动啦')
      
      
      t = Thread(target=func, args=(1, 2, 3), kwargs={"name": 'jiangwei'})
      t.start()
      print('开启了一个线进程')
      
    • 类创建

      from threading import Thread
      
      
      class MyThread(Thread):
          def __init__(self, i):
              super(MyThread, self).__init__()
              self.i = i
      
          def run(self):
              time.sleep(1)
              print(self.i)
      
      
      t = MyThread(10)
      t.start()
      
      

    线程相关方法和属性

    Thread对象的属性和方法

    • isAlive(): 返回线程是否活动的。
    • getName(): 返回线程名。
    • setName(): 设置线程名。

    threading模块提供的一些方法:

    • threading.currentThread(): 返回当前的线程变量。
    • threading.enumerate(): 返回一个包含正在运行的线程的list。正在运行指线程启动后、结束前,不包括启动前和终止后的线程。
    • threading.activeCount(): 返回正在运行的线程数量,与len(threading.enumerate())有相同的结果。

    守护线程

    无论是进程还是线程,都遵循:守护xxx会等待主xxx运行完毕之后被销毁

    注意:运行完毕并非终止运行

    • 对进程来说:指主进程代码运行完毕
    • 对线程来说:指主线程所在的进程内所有的非守护线程都运行完毕,主线程才算运行完毕,因为主线程的结束代表主进程的结束,进程整体的资源都将被回收,而进程必须保证非守护线程都运行完毕才能结束。
    def f1():
        print('f1', 666)
        time.sleep(3)
        print('你都没死,我咋能先死,你先把其他人杀了再说')
        time.sleep(2.5)
        print('我发现我已经死了')
    
    
    def f2():
        print('f2', 999)
        time.sleep(5)
        print('哈哈哈,他把我杀了')
    
    
    t1 = Thread(target=f1)
    t2 = Thread(target=f2)
    t1.daemon = True  # 守护线程t1
    t1.start()
    t2.start()
    print('主')       # 主线程结束(等待其他非守护进程结束,也就是f2运行完毕)、守护进程随之结束(f1不会执行到print)   
    """
    f1 666
    f2 999
    主
    你都没死,我咋能先死,你先把其他人杀了再说
    哈哈哈,他把我杀了
    """
    

    GIL全局解释器锁

    • 本质也是一个互斥锁,不是Python的特性,是Cpython解释器的特性
    • 在同一个进程下开启的多线程,同一时刻只能有一个线程能拿到那把锁,只有一个线程然后去执行
    • 不同的锁保护不同的数据,自定义的互斥锁,保护程序内的数据,而GIL锁是解释器级别的,与垃圾回收的数据有关
    • 开启多线程之后,先抢GIL,后抢自定义锁

    为什么要有这把锁

    解释器的代码是共享的,如果程序中有一个线程是修改全局变量n=100,而解释器里的垃圾回收线程执行回收n=100的操作,这就导致了数据混乱,不安全。

    疑问

    有了GIL的存在,同一时刻同一进程中只有一个线程被执行。那么,进程可以利用多核,但开销大,而线程开销小,却无法利用多核?

    其实不然:
    我开了一个工厂。假设我的原材料充足的情况下,我的工人越多越好,效率快。类似于cpu做计算.但是,如果我的原料不充足,要从西伯利亚运过来,那么我的工人越多越好吗?无济于事。类似于cpu做io读写操作。

    结论

    • 多线程用于IO密集型,如socket,爬虫,web
    • 多进程用于计算密集型,如金融分析,科学计算

    死锁与递归锁

    死锁

    两个或两个以上的进程或线程在执行过程中,因为争夺资源而造成的互相等待的现象。若无外力作用,他们将无法推进下去,会进入死锁状态

    递归锁Rlock

    可以多次acquire,内部维护一个Lock和counter变量。counter记录acquire的次数,每加锁一次就+1。直到该线程的所有锁被释放,其他线程才能抢到

    import time
    from threading import Lock, Thread, RLock
    
    # l1 = Lock()
    # l2 = Lock()
    
    
    # 递归锁  可以连续acquire多次,每acquire一次,计数器+1  只要计数不为0,就不能被其他线程抢到
    l1 = l2  = RLock()
    
    class MyThread(Thread):
        def run(self):
            self.f1()
            self.f2()
    
        def f1(self):
            l1.acquire()
            print('%s拿到了l1' % self.name)
            l2.acquire()
            print('%s拿到了l2' % self.name)
            l2.release()
            l1.release()
    
        def f2(self):
            l2.acquire()
            print('%s拿到了l1' % self.name)
            time.sleep(0.1)
            l1.acquire()
            print('%s拿到了l2' % self.name)
            l1.release()
            l2.release()
    
    
    if __name__ == '__main__':
        for i in range(10):
            t = MyThread()
            t.start()
    
    

    信号量

    也是一把锁,放多个钥匙。只不过不再是家里的卫生间,而是公共厕所,每次能进入多人。

    import time, random
    from threading import Thread, Semaphore, currentThread
    
    sm = Semaphore(5)
    
    
    def task():
        with sm:
            print('%s ing ' % currentThread().getName())
            time.sleep(random.randint(1, 3))
    
    
    if __name__ == '__main__':
        for i in range(10):
            t = Thread(target=task)
            t.start()
    

    事件

    主要是根据状态来控制线程

    常用方法

    • e.isSet()/e.is_set():返回event的状态值;
    • e.wait():如果 event.isSet()==False将阻塞线程;
    • e.set(): 设置event的状态值为True,所有阻塞池的线程激活进入就绪状态,等待操作系统调度;
    • e.clear():恢复event的状态值为False;
    import time
    from threading import Thread, Event
    
    event = Event()
    
    
    def student(name):
        print('%s 正在听课' % name)
        event.wait()
        # event.wait(2)  # 时间
        print('%s 课间活动' % name)
    
    
    def teacher(name):
        print('%s 正在讲课' % name)
        time.sleep(7)
    
        event.set()  # 改变状态为True,默认false event.clear()设为false
    
    
    if __name__ == '__main__':
        t = Thread(target=teacher, args=('jw',))
        s1 = Thread(target=student, args=('alex',))
        s2 = Thread(target=student, args=('wupeiqi',))
        s3 = Thread(target=student, args=('egon',))
        t.start()
        s1.start()
        s2.start()
        s3.start()
    

    定时器

    from threading import Timer
    
    def task(name):
        print('hello %s'%name)
    
    t = Timer(3,task,args=('egon',))
    t.start()
    

    线程队列

    import queue
    
    q = queue.Queue() # 先进先出--队列
    
    q1 = queue.LifoQueue()  # 后进先出--堆栈
    
    q2 = queue.PriorityQueue()  # 优先级队列
    
    q2.put('a')
    q2.put('c')
    q2.put('a')
    
    print(q2.get())
    print(q2.get())
    print(q2.get())
    

    线程池

    基本方法

    • t.submit(func,*args,**kwargs):异步提交任务
    • t.shutdown(wait=True):pool.close() + pool.join()
    • t.result():拿结果
    • t.add_done_callback(func):添加回调函数

    使用

    import time, os, random
    
    from concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutor
    
    
    def task(name):
        print('name: %s pid:%s run' % (name, os.getpid()))
        time.sleep(random.randint(1, 3))
        return '我是回调函数'
    
    
    def call_back(m):
        print(m.result())
    
    
    if __name__ == '__main__':
        pool = ThreadPoolExecutor(5)
        t_list = []
        for i in range(10):
            t = pool.submit(task, 'alex%s' % i)
            t.add_done_callback(call_back)
            t_list.append(t)
        pool.shutdown(wait=True)  # close + join
        print('主')
        for t in t_list:
            print('----', t.result())
    
    
  • 相关阅读:
    Atitit.eclise的ide特性abt 编译
    Atitit python3.0 3.3 3.5 3.6 新特性 Python2.7新特性1Python 3_x 新特性1python3.4新特性1python3.5新特性1值得关注的新特性1Pyth
    Atitit. Atiposter 发帖机 新特性 poster new feature   v7 q39
    Atitit.eclipse 4.3 4.4  4.5 4.6新特性
    atitit.错误:找不到或无法加载主类 的解决 v4 qa15.doc
    Atitit RSA非对称加密原理与解决方案
    Atitti.数字证书体系cer pfx attilax总结
    Atitit ftp原理与解决方案
    Atitit qzone qq空间博客自动点赞与评论工具的设计与实现
    Atitit 软件国际化原理与概论
  • 原文地址:https://www.cnblogs.com/xjmlove/p/10392005.html
Copyright © 2011-2022 走看看