zoukankan      html  css  js  c++  java
  • 多线程

    1.概念

      1.1 进程:(有时也称为重量级进程),是一个执行中的程序。每个程序都有自己的地址空间、内存、数据栈以及其他用于跟踪执行的辅助数据。

      1.2 线程:线程是操作系统够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。

      1.3全局解释器锁: 尽管Python解释器中可以运行多个线程,但是在任意给定的时刻,只有一个线程会被解释器执行。这就是由全局解释器锁控制的(GIL)。

      多线程的环境中,Python虚拟机会按照如下的步骤执行(借鉴与Python核心编程):

        a.设置GIL

        b.切换一个线程进去

        c.执行如下操作之一:(1).指定数量的得字节码指令。(2).线程主动让出控制权

        d.把线程设置睡眠状态(切换出线程)

        e.解锁GIL

        f.重复上述步骤

      自我理解:任意时刻,一个进程中只会有一个线程被Python解释器调用,并且随机,排队中的线程获得下次GIL也是随机的。这样看来Python中的多线程是否多余了呢?其实不然,相对于计算密集型的线程可能不太能感受到多线程的效果,但IO密集型的线程就会有很好的体验。在Python中。这个可以用多进程,或者协程进行弥补,但是多进程又要考虑到进程之间的数据交流问题,下面谈!!!!

    2.多线程(threading模块)

     2.1 Thread 的属性和方法

      Atttribute:

        name ;线程名

        ident: 线程的标识符  

        daemon: 布尔标志,表示这个线程是否为守护线程

      Method:

        start() 开始执行线程

        run() 定义线程功能的方法(通常在子类中应用被开发者重写)

        join(timeout=None) 直至启动的线程终止之前一直挂起;除非给出了timeout,否则会一直阻塞

        getName() 返回线程名 

        setName(name) 设定线程名

        setDaemon() 把线程的守护标志设定为布尔值TRUE (必须在线程开始start()之前调用)

    2.2.两种创建方式  

      a.创建Thread实例,传给他一个函数

      

    import threading
    import time
     
    def func(num): #定义每个线程要运行的函数
     
        print("running on number:%s" %num)
     
        time.sleep(3)
     
    if __name__ == '__main__':
     
        t1 = threading.func(target=sayhi,args=(1,)) #生成一个线程实例
        t2 = threading.func(target=sayhi,args=(2,)) #生成另一个线程实例
     
        t1.start() #启动线程
        t2.start() #启动另一个线程
     
        print(t1.getName()) #获取线程名
        print(t2.getName())
    

       b.派生Thread的子类,并创建子类的实例

     

    import threading
    import time
     
     
    class MyThread(threading.Thread):
        def __init__(self,num):
            threading.Thread.__init__(self)
            self.num = num
     
        def run(self):#定义每个线程要运行的函数
     
            print("running on number:%s" %self.num)
     
            time.sleep(3)
     
    if __name__ == '__main__':
     
        t1 = MyThread(1)
        t2 = MyThread(2)
        t1.start()
        t2.start()

    2.3 join() 与 setDaemon()

      setDaemon() :将线程声明为守护线程,必须在start() 方法调用之前设置, 如果不设置为守护线程程序会被无限挂起。这个方法基本和join是相反的。当我们 在程序运行中,执行一个主线程,如果主线程又创建一个子线程,主线程和子线程 就分兵两路,分别运行,那么当主线程完成想退出时,会检验子线程是否完成。如 果子线程未完成,则主线程会等待子线程完成后再退出。但是有时候我们需要的是 只要主线程完成了,不管子线程是否完成,都要和主线程一起退出,这时就可以 用setDaemon方法啦 

      join():在子线程完成运行之前,这个子线程的父线程将一直被阻塞。

    3.同步锁(Lock)

     问题:多个线程都在操作一个共享资源,造成了资源破坏(也就是,当一个线程的数据为未处理结束,其他线程已经开始)

     不用join的原因:join会把整个线程给停住,失去了多线程的意义

    import time
    import threading
    
    def addNum():
        global num #在每个线程中都获取这个全局变量
        # num-=1
        lock.acquire()
        temp=num
        print('--get num:',num )
        #time.sleep(0.1)
        num =temp-1 #对此公共变量进行-1操作
        lock.release()
    
    num = 100  #设定一个共享变量
    thread_list = []
    lock=threading.Lock()#通过创建Lock对象获得锁
    
    for i in range(100):
        t = threading.Thread(target=addNum)
        t.start()
        thread_list.append(t)
    
    for t in thread_list: #等待所有线程执行完毕
        t.join()
    
    print('final num:', num )

    4.线程死锁和递归锁

      死锁:在线程间共享多个资源的时候,如果两个线程分别占有一部分资源并且同时等待对方的资源,就会造成死锁

    def dotA():
        lockA.acquire()
        lockB.acquire()
        '''
        数据操作    
        '''
        lockB.realse()
        lockA.realse()
    def dotB():
        lockB.acquire()
        lockA.acquire()
        '''
        数据操作    
        '''
        lockA.realse()
        lockB.realse()
    此时lockA与lockB会互相的等待对方,造成死锁

      解决方式:使用递归锁

      为了支持在同一线程多次请求同一资源,Python提供了"可重入锁",threading.RLock,Rlock内部维护着一个Lock和一个counter变量,counter记录了acquire的次数,从而使得资源可以被多次acquire(),直到一个线程的所有的acquire都被relase,其他线程才能获得资源;将lock全部用threading.Rlock

    5.条件同步变量

       有一类线程需要满足条件之后才能够继续执行,Python提供了threading.Condition 对象用于条件变量线程的支持,它除了能提供RLock()或Lock()的方法外,还提供了 wait()、notify()、notifyAll()方法。

    wait():条件不满足时调用,线程会释放锁并进入等待阻塞;
    notify():条件创造后调用,通知等待池激活一个线程;
    notifyAll():条件创造后调用,通知等待池激活所有线程。
    

      

    import threading,time
    from random import randint
    class Producer(threading.Thread):
        def run(self):
            global L
            while True:
                val=randint(0,100)
                print('生产者',self.name,":Append"+str(val),L)
                if lock_con.acquire():
                    L.append(val)
                    lock_con.notify()
                    lock_con.release()
                time.sleep(3)
    class Consumer(threading.Thread):
        def run(self):
            global L
            while True:
                    lock_con.acquire()
                    if len(L)==0:
                        lock_con.wait()
                    print('消费者',self.name,":Delete"+str(L[0]),L)
                    del L[0]
                    lock_con.release()
                    time.sleep(0.25)
    
    if __name__=="__main__":
    
        L=[]
        lock_con=threading.Condition()
        threads=[]
        for i in range(5):
            threads.append(Producer())
        threads.append(Consumer())
        for t in threads:
            t.start()
        for t in threads:
            t.join()

    6.同步条件:

    event.isSet():返回event的状态值;
    
    event.wait():如果 event.isSet()==False将阻塞线程;
    
    event.set(): 设置event的状态值为True,所有阻塞池的线程激活进入就绪状态, 等待操作系统调度;
    
    event.clear():恢复event的状态值为False。
    

      

    from threading import Event
    from threading import Thread
    import time
    
    def boss(event):
        print('今天晚上加班!!!')
        event.isSet() or event.set()
        time.sleep(5)
        print("下班了!!!")
        event.isSet() or event.set()
    def worker(event):
        event.wait()
        print("命苦呀!!!!")
        event.clear()
        event.wait()
        print('欧耶!!!!')
    def main():
        event = Event()
        event.clear()
        threads = []
        for i in range(4):
            threads.append(Thread(target=worker,args=(event,)))
        threads.append(Thread(target=boss,args=(event,)))
        for thread in threads:
            thread.start()
        for thread in threads:
            thread.join()
        print('all done')
    if __name__ == '__main__':
        main()

    7.队列(Queue)

    创建一个“队列”对象
    import Queue
    q = Queue.Queue(maxsize = 10)
    Queue.Queue类即是一个队列的同步实现。队列长度可为无限或者有限。可通过Queue的构造函数的可选参数maxsize来设定队列长度。如果maxsize小于1就表示队列长度无限。
    
    将一个值放入队列中
    q.put(10)
    调用队列对象的put()方法在队尾插入一个项目。put()有两个参数,第一个item为必需的,为插入项目的值;第二个block为可选参数,默认为
    1。如果队列当前为空且block为1,put()方法就使调用线程暂停,直到空出一个数据单元。如果block为0,put方法将引发Full异常。
    
    将一个值从队列中取出
    q.get()
    调用队列对象的get()方法从队头删除并返回一个项目。可选参数为block,默认为True。如果队列为空且block为True,get()就使调用线程暂停,直至有项目可用。如果队列为空且block为False,队列将引发Empty异常。
    
    Python Queue模块有三种队列及构造函数:
    1、Python Queue模块的FIFO队列先进先出。  class queue.Queue(maxsize)
    2、LIFO类似于堆,即先进后出。             class queue.LifoQueue(maxsize)
    3、还有一种是优先级队列级别越低越先出来。   class queue.PriorityQueue(maxsize)
    
    此包中的常用方法(q = Queue.Queue()):
    q.qsize() 返回队列的大小
    q.empty() 如果队列为空,返回True,反之False
    q.full() 如果队列满了,返回True,反之False
    q.full 与 maxsize 大小对应
    q.get([block[, timeout]]) 获取队列,timeout等待时间
    q.get_nowait() 相当q.get(False)
    非阻塞 q.put(item) 写入队列,timeout等待时间
    q.put_nowait(item) 相当q.put(item, False)
    q.task_done() 在完成一项工作之后,q.task_done() 函数向任务已经完成的队列发送一个信号
    q.join() 实际上意味着等到队列为空,再执行别的操作
    

      

  • 相关阅读:
    2017/07/25 工作日志
    2017/07/27 工作日志
    2017/07/31 工作日志
    2017/07/26 工作日志
    2017/07/28 工作日志
    远程客户端由于元数据地址主机名为服务器计算机名而无法解析WCF服务元数据的解决办法
    两步实现SQLSERVER版本降级
    dll版本号相同,提示加载dll失败
    silverlight登陆页面的小细节【自动设置焦点,回车登陆】
    Silverlight向aspx传值
  • 原文地址:https://www.cnblogs.com/mdevelopment/p/9431475.html
Copyright © 2011-2022 走看看