• day36,多线程,线程中的锁机制,joinablequeue


                                                                                      多线程

    什么是线程 :是真正的执行单位

    线程不能单独存在 必须存在于进程中,

    进程是一个资源单位,其包含了运行程序所需的所有资源

    线程才是真正的执行单位

    没有线程,进程中的资源无法被利用起来,所以一个进程至少包含一个线程,称之为主线程

    当我们启动一个程序时,操作系统就会自己为这个程序创建一个主线程

    线程可以由程序后期开启 ,自己开启线程称之为子线程

    为什么需要线程:

    它的目的只有一个就是提高效率,

    因为在是在进程中,只有一个线程的话效率很低,多个线程就可以完成更多的工作,

    还有一个原因就是,在创建多个进程来完成的话,就会及其的消耗资源

    如何使用创建线程:

    两种方法

       一个使用Threading 类中的Thread 创建线程

       第二个方法是继承Thread这个类覆盖run方法创建线程,这个和进程的创建是差不多的

    实例:第一种方法

    from threading import Thread,current_thread
    import time
    
    def task():
        print("2",current_thread())
        print("子线程running")
        time.sleep(10)
        print("子线程over")
    
    # 使用方法一  直接实例化Thread类
    if __name__ == '__main__':
        t = Thread(target=task)
        t.start()
        
        # task()
        # 执行顺序不固定 如果开启线程速度足够快  可能子线程先执行
        print("主线程over")
        print("1",current_thread())

    实例:第二种方法

    # 使用方法二 继承Thread 覆盖run方法
    class MyThread(Thread):
        def run(self):
            print("子线程run!")
    m = MyThread()
    print("主线over")
    
    # 使用方法和多进程一模一样   开启线程的代码可以放在任何位置  开启进程必须放在判断下面

    线程的几个特点:
        1 开启线程不消耗资源,运行速度比叫进程要快

        2 在开启多个子线程后,线程之间的数据是共享的,所以就不存在什么进程中的queue和Manager 

        3 线程中是没有父子级的,都是相同的pid不存在谁要先执行,后执行的,都是通过自己抢占的,就算是有父子级,都是自己设置的

    守护线程:

    守护线程是和进程中的守护进程是一样的,就是被守护的线程如果死掉的话,那么守护线程也会随着死掉

    JoinAbleQueue

    这是一个相当一是Queue这样的一个列队,joinablequeue

      不同的是队列允许项目的使用者通知生成者项目已经被成功处理。通知进程是使用共享的信号和条件变量来实现的。

    joinablequeue 中间需要注意的是在获取数据是需要使用 q.task_done()这个方法告诉进程,获取的每一个值,知道列队中的值被取完

    就算值取完了,列队也是会等待在添加值,然后在取,这样进程是不会结束的,所以就把这个进程设置为守护进程,通过被守护的进程结束

    那么,守护进程也会结束,看这样整个程序就结束了

    JoinAbleQueue 实例:
    from multiprocessing import Process,JoinableQueue
    import time,random
    def consumer(q):
        while True:
            time.sleep(random.randint(1,5))
            res=q.get()
            print('消费者拿到了 %s' %res)
            q.task_done()
    
    def producer(seq,q):
        for item in seq:
            time.sleep(random.randrange(1,2))
            q.put(item)
            print('生产者做好了 %s' %item)
        q.join()
    
    if __name__ == '__main__':
        q=JoinableQueue()
        seq=('包子%s' %i for i in range(10))
        p=Process(target=consumer,args=(q,))
        p.daemon=True #设置为守护进程,在主线程停止时p也停止,但是不用担心,producer内调用q.join保证了consumer已经处理完队列中的所有元素
        p.start()
    
        producer(seq,q)
    
        print('主线程')

    线程中的锁

    共享意味着竞争

    线程中也存在安全问题,

    多线程可以并发执行,一旦并发了并且访问了同一个资源就会有问题

    解决方案:还是互斥锁

    from threading import Thread,enumerate,Lock
    import time
    
    number = 10
    
    lock = Lock()
    
    def task():
        global number
        lock.acquire()
        a = number
        time.sleep(0.1)
        number = a - 1
        lock.release()
    
    for i in range(10):
        t = Thread(target=task)
        t.start()
    
    for t in enumerate()[1:]:
        # print(t)
        t.join()
        
    print(number)
    View Code

    死锁:

    死锁就是在有多个锁的多个线程中,一个资源需要多重锁来锁住资源,那么有多个线程抢占这个资源,就会发生一个线程抢占一个锁,

    满足不了,这个资源需要双重锁,这样就会发生,死锁的问题,都不解不了锁,所以程序就只能等待下去

    可重入锁

    Rlock 同一个线程可以多次执行acquire,释放锁时,有几次acquire就要release几次。

    但是本质上同一个线程多次执行acquire时没有任何意义的,其他线程必须等到RLock全部release之后才能访问共享资源。

    所以Rlock仅仅是帮你解决了代码逻辑上的错误导致的死锁,并不能解决多个锁造成的死锁问题

    Rlock 的实例:

    # 同一把RLock 多次acquire
    #l1 = RLock()
    #l2 = l1
    
    # 不同的RLock 依然会锁死
    import threading
    from threading import Thread
    from multiprocessing import RLock
    
    l1 = RLock()
    l2 = RLock()
    
    def task():
        l1.acquire()
        print(threading.current_thread().name,"拿到了筷子")
        l2.acquire()
        print(threading.current_thread().name, "拿到了盘子")
    
        print("吃饭")
        l1.release()
        l2.release()
    
    def task2():
        l2.acquire()
        print(threading.current_thread().name, "拿到了盘子")
    
        l1.acquire()
        print(threading.current_thread().name,"拿到了筷子")
    
        print("吃饭")
    
        l2.release()
        l1.release()
    
    t1 = Thread(target=task)
    t1.start()
    t2 = Thread(target=task2)
    t2.start()
    View Code

     

    信号量:

    Semaphore

    信号量也是一种锁,其特殊之处在于可以让一个资源同时被多个线程共享,并控制最大的并发访问线程数量。

    Semaphore 是用于用户访问服务器的最大的并发量,可以限制访问的人数,一般不用于线程

    如果把Lock比喻为家用洗手间,同一时间只能一个人使用。

    那信号量就可以看做公共卫生间,同一时间可以有多个人同时使用。

    from threading import  Thread,Semaphore,current_thread
    import time
    
    s = Semaphore(3)
    def task():
        s.acquire()
        print("%s running........" % current_thread())
        time.sleep(1)
        s.release()
        
    for i in range(20):
        Thread(target=task).start()
  • 相关阅读:
    ASP.NET SignalR HubPipelineModule
    MongoDB新版本特性
    Xamarin向iOS和Android引入C# Async支持
    Redis开源文档《Redis设计与实现》[转]
    WCF的追踪分析工具——SvcPerf
    Windows Azure移动服务更新,支持Android、活动目录和更多语言,并支持在东亚地区部署服务
    社区网站系统 jsGen
    单元测试同时支持 NUnit/MSTest
    .NET的微型Web框架 Nancy
    通过二维码登录(CSC模式)
  • 原文地址:https://www.cnblogs.com/WBaiC1/p/10976042.html
Copyright © 2020-2023  润新知