• 多线程


    线程和进程的区别:

    进程之间切换非常消耗资源

    线程之间切换相对来说节省资源

     

    使用线程的场景:

    多进程和多线程的数据共享的区别

    # 多进程  最后执行打印结果两个100
    from multiprocessing import Process
    def func1():
        global g
        g = 0
    
    if __name__ == '__main__':
        g = 100
        print(g)
        p = Process(target=func1)
        p.start()
        p.join()
        print(g)
    
    
    
    # 多线程  最后执行打印结果两个第一个100,第二个0
    from threading import Thread def func1(): global g g = 0 if __name__ == '__main__': g = 100 print(g) t = Thread(target=func1) t.start() t.join() print(g)

      

    全局解释锁GIL锁住的不是线程共享的数据,而是所有的进程。

    虽然有了全局解释锁GIL,但是还不是最安全的,因为比如以下情况:

    这种情况下我想着线程有了全局解释锁,最后结果应该是0,打印出来结果却是9,因为中间我们人为让程序睡眠了一秒(真实情况下可能由于时间片轮转到了下一个线程),

    所以这个时候就需要加锁了。

    科学家吃面引出来的死锁问题:

    from threading import Thread, Lock
    import time
    noodle_lock = Lock()
    fork_look = Lock()
    def eat1(name):
        noodle_lock.acquire()
        print("{}拿到面了".format(name))
        fork_look.acquire()
        print("{}拿到叉子了".format(name))
        print("{}开始吃面了".format(name))
        noodle_lock.release()
        fork_look.release()
    
    
    def eat2(name):
        fork_look.acquire()
        print("{}拿到叉子了".format(name))
        time.sleep(1)
        noodle_lock.acquire()
        print("{}拿到面了".format(name))
        print("{}开始吃面了".format(name))
        fork_look.release()
        noodle_lock.release()
    
    if __name__ == '__main__':
        Thread(target=eat1, args=("alex", )).start()
        Thread(target=eat2, args=("egon", )).start()
        Thread(target=eat1, args=("nezha", )).start()
        Thread(target=eat2, args=("bossjin", )).start()
    

      

    科学家吃面引出来的递归锁问题:

    from threading import Thread, RLock
    import time
    fork_look = noodle_lock = RLock()
    def eat1(name):
        noodle_lock.acquire()
        print("{}拿到面了".format(name))
        fork_look.acquire()
        print("{}拿到叉子了".format(name))
        print("{}开始吃面了".format(name))
        noodle_lock.release()
        fork_look.release()
    
    
    def eat2(name):
        fork_look.acquire()
        print("{}拿到叉子了".format(name))
        time.sleep(1)
        noodle_lock.acquire()
        print("{}拿到面了".format(name))
        print("{}开始吃面了".format(name))
        fork_look.release()
        noodle_lock.release()
    
    if __name__ == '__main__':
        Thread(target=eat1, args=("alex", )).start()
        Thread(target=eat2, args=("egon", )).start()
        Thread(target=eat1, args=("nezha", )).start()
        Thread(target=eat2, args=("bossjin", )).start()
    

      

     

    同一个进程或线程中用到两把或两把以上锁的时候,就容易产生死锁现象,这个时候把互斥锁改为递归锁,就能规避这种情况。

    RLock内部维护着一个Lock和一个counter变量,counter记录了acquire的次数,从而使得资源可以被多次require。直到一个线程所有的acquire都被release,其他的线程才能获得资源。

    进程中的数据本身就不共享,所以很少会用到加锁的情况,而线程则是数据共享的,所以需要加锁  

    线程中的事件例子:

    from threading import Thread, Event
    import random
    import time
    
    e = Event()
    
    def check_web():
        time.sleep(random.randint(3, 6))
        e.set()
    
    def conn_db():
        count = 1
        while count < 4:
            e.wait(1)
            if e.is_set():
                print("连接数据库成功")
                break
            else:
                print("第{}次连接数据库失败".format(count))
                count += 1
        else:
            raise TimeoutError("连接数据库超时")
    
    
    
    if __name__ == '__main__':
        Thread(target=check_web).start()
        Thread(target=conn_db).start()
    

      

    线程中的定时器

    from threading import Thread, Timer
    def func():
        print("五秒后的线程开启")
    
    if __name__ == '__main__':
        Timer(interval=5, function=func).start()
    

      

    线程池中的队列:

    # 多线程中的数据已经是共享的了,为什么还要用队列?
    # 比如a线程要修改共享的一个列表中的一个元素,刚拿回来数据准备修改,时间片到了b线程,
    # 这个时候b又去修改这个元素,就会造成数据的不安全,这个时候可以想到应该加锁,
    # 而队列中就是加好锁了,供我们使用。
    
    
    import queue
    q = queue.Queue()  #队列,先进先出
    q.put(1)
    q.put(2)
    print(q.get())
    
    
    q = queue.LifoQueue()  #栈,先进后出
    q.put(1)
    q.put(2)
    print(q.get())
    
    
    q = queue.PriorityQueue()  #优先级队列
    q.put((10, "a"))
    q.put((1, "b"))
    q.put((8, "c"))
    print(q.get())
    

      

    线程池

    # 线程池里面的线程个数最多不要超过cpu个数乘以五
    from concurrent.futures import ThreadPoolExecutor
    import time
    
    
    def func(n):
        print(n)
        time.sleep(1)
        return n * n
    
    if __name__ == '__main__':
        pool = ThreadPoolExecutor(max_workers=5)
        for i in range(1, 21):
            p = pool.submit(func, i)
        pool.shutdown() #shutdown起到了close和join两个效果,close不让往线程池中扔任务,join等待线程池中的任务执行完毕
        print("主线程")
    

      

  • 相关阅读:
    MySQL详细操作
    啥是SQL?
    MySQL之中文乱码问题
    Windows压缩包安装MySQL
    GIL(全局解释器锁)
    协程
    线程
    第八周 编程作业
    PCA
    第八周 第一部分
  • 原文地址:https://www.cnblogs.com/QimiSun/p/10578002.html
Copyright © 2020-2023  润新知