pythonx4-多线程02

多线程-2

  1. 多线程的几个概念

    1. 并行, parallel
      互不干扰的在同一时刻做多件事;
      如,同一时刻,同时有多辆车在多条车道上跑,即同时发生的概念.
    2. 并发, concurrency
      同时做某些事,但是强调同一时段做多件事.
      如,同一路口,发生了车辆要同时通过路面的事件.
    3. 队列, 缓冲区

    类似排队,是一种天然解决并发的办法.排队区域就是缓冲区.
    解决并发:
    【 "食堂打饭模型", 中午12点,大家都涌向食堂,就是并发.人很多就是高并发.】

    1. 队列, 缓冲区:
      队列: 即排队.
      缓冲区: 排成的队列.
      优先队列: 如果有男生队伍和女生队伍,女生队伍优先打饭,就是优先队列.

    2. 争抢:
      锁机制: 争抢打饭,有人抢到,该窗口在某一时刻就只能为这个人服务,锁定窗口,即锁机制.
      争抢也是一种高并发解决方案,但是有可能有人很长时间抢不到,所以不推荐.

    3. 预处理:

      统计大家爱吃的菜品,最爱吃的80%热门菜提前做好,20%冷门菜现做,这样即使有人锁定窗口,也能很快释放.

      这是一种提前加载用户需要的数据的思路,预处理思想,缓存常用.

    4. 并行:

      开多个打饭窗口,同时提供服务.

      IT日常可以通过购买更多服务器,或多开线程,进程实现并行处理,解决并发问题.

      这是一种水平扩展的思路.

      注: 如果线程在单CPU上处理,就不是并行了.

    5. 提速:

      通过提高单个窗口的打饭速度,也是解决并发的方式.

      IT方面提高单个CPU性能,或单个服务器安装更多的CPU.

      这是一种垂直扩展的思想.

    6. 消息中间件:

      如上地地铁站的九曲回肠的走廊,缓冲人流.

      常见消息中间件: RabbitMQ, ActiveMQ(Apache), RocketMQ(阿里Apache), kafka(Apache)等.

  2. 多线程案例

     import threading
    
     import time
     
     
     def worker():
         count = 0
     
         while True:
     
             if (count >= 5):
                 break
     
             time.sleep(1)
     
             count += 1
             print(threading.current_thread())
             print('worker running')
     
     
     class MyThread(threading.Thread):
     
         def start(self):
             print('start~~~~~~~~~~~~~')
     
             super().start()
     
         def run(self):
             print('run~~~~~~~~~~~~~~~~~')
     
             super().run()
     
     
     t1 = MyThread(name='worker', target=worker)
     t2 = MyThread(name='boss', target=worker)
     
     t1.start()
     t2.start()
     
     # t1.run()
     # t2.run()
    

    没有开新的线程,这就是普通函数调用,所以执行完t1.run(),然后执行t2.run(),这里就不是多线程。

    当使用start方法启动线程后,进程内有多个活动的线程并行的工作,就是多线程。

    一个进程中至少有一个线程,并作为程序的入口,这个线程就是主线程。一个进程至少有一个主线程。

    其他线程称为工作线程。

  3. 后台线程和前台线程

    is/setDaemon(bool): 获取/设置是后台线程(默认前台线程(False))。(在start之前设置)

    1. 如果是后台线程(当我们使用setDaemon(True)方法,设置子线程为守护线程时),主线程执行过程中,后台线程也在进行,主线程执行完毕后,后台线程不论成功与否,主线程和后台线程均停止

    2. 如果是前台线程(其实就是setDaemon(False)),主线程执行过程中,前台线程也在进行,主线程执行完毕后,等待前台线程也执行完成后,程序停止

        if __name__ == '__main__':
            for i in range(4):
                t = MyThread(i)
                t.setDaemon(True)
                t.start()
            print('main thread end!')
  1. join()

    join()阻塞当前上下文环境的线程,直到调用此方法的线程终止或到达指定的timeout,即使设置了setDeamon(True)主线程依然要等待子线程结束。

    线程必须先start()然后再join()

    错误的做法是

     if __name__ == '__main__':
         for i in range(4):
             t = MyThread(i)
             t.start()
             t.join()
    

    可以看出此时,程序只能顺序执行,每个线程都被上一个线程的join阻塞,使得“多线程”失去了多线程意义。

    ☝️这样

     if __name__ == '__main__':
         th=[]
         for i in range(4):
             t = MyThread(i)
             th.append(t)
             t.start()
     
     
         for tt in th:
             tt.join()
         #设置join之后,主线程等待子线程全部执行完成后或者子线程超时后,主线程才结束
         print('main thread end!')
    

    join的原理就是依次检验线程池中的线程是否结束,没有结束就阻塞直到线程结束,如果结束则跳转执行下一个线程的join函数。

    先看看这个:

    1. 阻塞主进程,专注于执行多线程中的程序。
    2. 多线程多join的情况下,依次执行各线程的join方法,前头一个结束了才能执行后面一个。
    3. 无参数,则等待到该线程结束,才开始执行下一个线程的join。
    4. 参数timeout为线程的阻塞时间,如 timeout=2 就是罩着这个线程2s 以后,就不管他了,继续执行下面的代码

线程安全

  1. 线程同步-Lock、Rlock类

    先上一段代码:

     import threading
     import time
     zero = 0
     def change_zero():
         global zero
         for i in range(5000000):
             zero =zero+ 1
             zero =zero- 1
     
     th1 = threading.Thread(target = change_zero)
     th2 = threading.Thread(target = change_zero)
     th1.start()
     th2.start()
     th1.join()
     th2.join()
     print(zero)
    

    可能在一个线程执行时,两步只执行了一步x = zero + 1(即还没来得及对zero进行修改),GIL锁就给了另一个线程(不熟悉锁的概念以及GIL的可以先看这篇文章),等到GIL锁回到第一个线程,zero已经发生了改变,这时再执行接下来的zero = x,结果就不是对zero加1了。

     import threading
     import time
     i=1
     def worker():
         global i
         print('当前的线程是{0},i等于{1}'.format(threading.current_thread(),i))
         time.sleep(1)
         i=i+1
     
         print('-------{0},i等于{1}'.format(threading.current_thread(), i))
     
     for x in range(1, 5):
         name = 'worker{}'.format(x)
     
         t = threading.Thread(name=name, target=worker)
     
         t.start()
    

    perfect:https://www.cnblogs.com/amesy/p/8067583.html

由于线程之间随机调度:某线程可能在执行n条后,CPU接着执行其他线程。为了多个线程同时操作一个内存中的资源时不产生混乱,我们使用锁。

Lock(指令锁)是可用的最低级的同步指令。Lock处于锁定状态时,不被特定的线程拥有。Lock包含两种状态——锁定和非锁定,以及两个基本的方法。

可以认为Lock有一个锁定池,当线程请求锁定时,将线程至于池中,直到获得锁定后出池。池中的线程处于状态图中的同步阻塞状态。

RLock(可重入锁)是一个可以被同一个线程请求多次的同步指令。RLock使用了“拥有的线程”和“递归等级”的概念,处于锁定状态时,RLock被某个线程拥有。拥有RLock的线程可以再次调用acquire(),释放锁时需要调用release()相同次数。

可以认为RLock包含一个锁定池和一个初始值为0的计数器,每次成功调用 acquire()/release(),计数器将+1/-1,为0时锁处于未锁定状态。

实例方法: 
    
* acquire([timeout]): 尝试获得锁定。使线程进入同步阻塞状态。 
  
* release(): 释放锁。使用前线程必须已获得锁定,否则将抛出异常。


    ```
    import threading
    import time
    
    
    # 方法一:将要执行的方法作为参数传给Thread的构造方法
    count=0
    lock = threading.RLock()
    def action(arg):
        lock.acquire() #获得锁
        
        #临界区开始
        time.sleep(1)
        global count
        count+=1
        
        print(threading.current_thread())
        count-=1
        print('the arg is:{0},count is:{1}'.format(arg,count))
        
        #临界区结束
        lock.release()
    ths=[]
    for i in range(4):
    
        t =threading.Thread(target=action,args=(i,))
        # t.setDaemon(True)
        ths.append(t)
    
    for tt in ths:
        tt.start()
    
    for tt in ths:
        tt.join()
    
    if __name__ == '__main__':
        # print(threading.current_thread())
        # print(threading.enumerate())
        # print(threading.activeCount())
        print('main thread end!')
    ```
  1. Lock对比Rlock

     import threading
     lock = threading.Lock() #Lock对象
     lock.acquire()
     lock.acquire()  #产生了死锁。
     lock.release()
     lock.release()
     print lock.acquire()
      
      
     import threading
     rLock = threading.RLock()  #RLock对象
     rLock.acquire()
     rLock.acquire() #在同一线程内,程序不会堵塞。
     rLock.release()
     rLock.release()
    
  2. Condition类

    Condition(条件变量)通常与一个锁关联。需要在多个Contidion中共享一个锁时,可以传递一个Lock/RLock实例给构造方法,否则它将自己生成一个RLock实例。

    可以认为,除了Lock带有的锁定池外,Condition还包含一个等待池,池中的线程处于等待阻塞状态,直到另一个线程调用notify()/notifyAll()通知;得到通知后线程进入锁定池等待锁定。

    构造方法:
    Condition([lock/rlock])

    实例方法:

    • acquire([timeout])/release(): 调用关联的锁的相应方法。

    • wait([timeout]): 调用这个方法将使线程进入Condition的等待池等待通知,并释放锁。使用前线程必须已获得锁定,否则将抛出异常。

    • notify(): 调用这个方法将从等待池挑选一个线程并通知,收到通知的线程将自动调用acquire()尝试获得锁定(进入锁定池);其他线程仍然在等待池中。调用这个方法不会释放锁定。使用前线程必须已获得锁定,否则将抛出异常。

    • notifyAll(): 调用这个方法将通知等待池中所有的线程,这些线程都将进入锁定池尝试获得锁定。调用这个方法不会释放锁定。使用前线程必须已获得锁定,否则将抛出异常。

        # encoding: UTF-8
        import threading
        import time
        
        # 商品
        product = None
        # 条件变量
        con = threading.Condition()
        
        
        # 生产者方法
        def produce():
            global product
        
            if con.acquire():
                while True:
                    if product is None:
                        print ('produce...')
                        product = 'anything'
        
                        # 通知消费者,商品已经生产
                        con.notify()
        
                    # 等待通知
                    # con.wait()
                    time.sleep(2)
        
        
        # 消费者方法
        def consume():
            global product
        
            if con.acquire():
                while True:
                    if product is not None:
                        print('consume...')
                        product = None
        
                        # 通知生产者,商品已经没了
                        con.notify()
        
                    # 等待通知
                    con.wait()
                    time.sleep(2)
        
        
        t1 = threading.Thread(target=produce)
        t2 = threading.Thread(target=consume)
        t2.start()
        t1.start()
      

      生产者消费者模型

        import threading
        import time
        
        condition = threading.Condition()
        products = 0
        
        class Producer(threading.Thread):
            def run(self):
                global products
                while True:
                    if condition.acquire():
                        if products < 10:
                            products += 1
                            print("Producer(%s):deliver one, now products:%s" %(self.name, products))
                            condition.notify()#不释放锁定,因此需要下面一句
                            condition.release()
                        else:
                            print("Producer(%s):already 10, stop deliver, now products:%s" %(self.name, products))
                            condition.wait()#自动释放锁定
                        time.sleep(2)
        
        class Consumer(threading.Thread):
            def run(self):
                global products
                while True:
                    if condition.acquire():
                        if products > 1:
                            products -= 1
                            print("Consumer(%s):consume one, now products:%s" %(self.name, products))
                            condition.notify()
                            condition.release()
                        else:
                            print("Consumer(%s):only 1, stop consume, products:%s" %(self.name, products))
                            condition.wait()
                        time.sleep(2)
        
        if __name__ == "__main__":
            for p in range(0, 2):
                p = Producer()
                p.start()
        
            for c in range(0, 3):
                c = Consumer()
                c.start()
      

      condition.notifyAll()

        import threading
      
        alist = None
        condition = threading.Condition()
        
        
        def doSet():
            if condition.acquire():
                while alist is None:
                    condition.wait()
                for i in range(len(alist))[::-1]:
                    alist[i] = 1
                    print(alist[i])
                condition.notify()
                condition.release()
        
        
        def doPrint():
            if condition.acquire():
                while alist is None:
                    condition.wait()
                for i in alist:
                    print(i)
        
                print()
                condition.notify()
                condition.release()
        
        
        def doCreate():
            global alist
            if condition.acquire():
                if alist is None:
                    alist = [0 for i in range(10)]
                    condition.notifyAll()
                condition.release()
        
        
        tset = threading.Thread(target=doSet, name='tset')
        tprint = threading.Thread(target=doPrint, name='tprint')
        tcreate = threading.Thread(target=doCreate, name='tcreate')
        tset.start()
        tprint.start()
        tcreate.start()
      
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 196,264评论 5 462
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 82,549评论 2 373
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 143,389评论 0 325
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,616评论 1 267
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,461评论 5 358
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,351评论 1 273
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,776评论 3 387
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,414评论 0 255
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,722评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,760评论 2 314
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,537评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,381评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,787评论 3 300
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,030评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,304评论 1 252
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,734评论 2 342
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,943评论 2 336

推荐阅读更多精彩内容

  • 线程 操作系统线程理论 线程概念的引入背景 进程 之前我们已经了解了操作系统中进程的概念,程序并不能单独运行,只有...
    go以恒阅读 1,625评论 0 6
  • 线程状态新建,就绪,运行,阻塞,死亡。 线程同步多线程可以同时运行多个任务,线程需要共享数据的时候,可能出现数据不...
    KevinCool阅读 783评论 0 0
  • 【threading模块详解】 模块基本方法 该模块定了的方法如下:threading.active_count(...
    奕剑听雨阅读 1,021评论 0 0
  • 关于Python多线程的概述 由于GIL的存在,Python的多线程在CPU密集型任务并没有多大的优势,任何Pyt...
    千鸟月读阅读 538评论 0 0
  • class threading.Thread(group=None,target=None,name=None,a...
    SkTj阅读 452评论 0 2