• python3 多线程


    进程——资源分配的最小单位,线程——程序执行的最小单位

    什么是进程?  

      程序的执行实例称为进程。每个进程都提供执行程序所需的资源。一个进程有一个虚拟地址空间,可执行代码,打开系统对象的句柄,安全上下文,一个独特的过程,pid标识符,环境变量,优先级类,最小和最大工作集大小,并且至少有一个执行线程。每个进程都是从一个线程开始的,通常被称为主主线程,但是可以创建额外的任何线程的线程。

    进程与线程的区别?

      线程共享内存空间,进程的内存是独立的,同一个进程的线程之间可以直接交流,两个进程想通信,必须通过一个中间代理来实现,创建新线程很简单, 创建新进程需要对其父进程进行一次克隆,一个线程可以控制和操作同一进程里的其他线程,但是进程只能操作子进程

    线程基本函数,

    run() 里面是需要执行的命令,

    start() 线程启动函数,

    join() 等待该线程结束,

    setDaemon(True) 设置为守护线程,设置守护线程需要放在线程启动前

    Lock() 线程锁,防止数据的不准确行,线程执行时添加互斥锁

    RLock() 递归锁,多个线程时需要添加递归锁,否则会出现锁死

    BoundedSemaphore() 信息量,允许同时执行的最大线程数

    threading.current_thread() ,当前线程;

    threading.active_count() 目前活跃的线程数

    1.简单线程的写法:

    import threading
    from time import sleep
    
    '''多线程 第一种写法'''
    def run(t):
        print("task-%s" %t)
        sleep(2)
        print("task-%s is done" %t)
    
    t1 = threading.Thread(target=run,args=("t1",)) #参数后面需要带逗号,不然会误解析为一个参数
    t2 = threading.Thread(target=run,args=("t2",))
    t1.start()
    t2.start()
    
    '''多线程 第二种写法'''
    class MyThread(threading.Thread):
        def __init__(self,t):
            super(MyThread, self).__init__()  #继承父类的init函数
            self.t = t
    
        def run(self):
            print("task-%s"%self.t)
            sleep(2)
            print("task-%s is done"%self.t)
    
    if __name__ == "__main__":
        t1 = MyThread("t1")
        t2 = MyThread("t2")
        t1.start()
        t2.start()
    
    '''多线程 第三种写法'''
    def run(t):
        print("task-%s" % t)
        sleep(2)
        print("task-%s is done" % t)
    
    for i in range(2):
        t = threading.Thread(target=run, args=("t1",)) # 参数后面需要带逗号,不然会误解析为一个参数
        t.start()
    

      这三种的运行结果都一样:

    task-t1
    task-t1
    task-t1 is done
    task-t1 is done
    
    Process finished with exit code 0
    

    2.计算所有线程运行的时间

    import threading
    import time
    
    '''多计算所有线程运行的时间,这里需要给每个线程添加join'''
    def run(t):
        print("task-%s" % t)
        time.sleep(2)
        print("task-%s is done" % t)
    
    st_time = time.time() #开始时间
    
    tups = [] #所有的线程对象列表
    for i in range(2):
        t = threading.Thread(target=run, args=("t1",)) # 参数后面需要带逗号,不然会误解析为一个参数
        t.start()
        tups.append(t)
    
    #所有线程启动后,添加join,等待结束后,主线程再继续
    for i in tups:
        i.join()
    
    print(time.time()-st_time) #需要时间
    

      运行结果:

    task-t1
    task-t1
    task-t1 is done
    task-t1 is done
    2.0008037090301514
    
    Process finished with exit code 0
    

    3.设置守护线程

    import threading
    import time
    
    '''程序本身就是主线程,守护线程是伴随主线程存在的,主线程关闭是不会等待守护线程,同时守护线程也会关闭'''
    def run(t):
        print("task-%s" % t)
        time.sleep(2)
        print("守护线程:", threading.current_thread())
        print("task-%s is done" % t)
    
    st_time = time.time() #开始时间
    
    tups = [] #所有的线程对象列表
    for i in range(10):
        t = threading.Thread(target=run, args=(i,)) # 参数后面需要带逗号,不然会误解析为一个参数
        t.setDaemon(True)  #设置为守护线程
        t.start()
    
    print("主线程:",threading.current_thread())
    print("运行时间",time.time()-st_time) #需要时间
    

      运行结果:

    task-0
    task-1
    task-2
    task-3
    task-4
    task-5
    task-6
    task-7
    task-8
    task-9主线程:
     <_MainThread(MainThread, started 6800)>
    运行时间 0.0
    
    Process finished with exit code 0
    

     3.子线程的相互交互

    同一个进程里面的线程共享同一块空间,可以共享内存中的资源
    由于python的多线程基于GIL(全局解释器锁)原理,是通过单核上下文切换实现的多线程 ,所以在多个子线程同时修改一个数据时,会存在这个还没改完就切换的情况,当时数据不准确,因此我们需要给每个子线程的操作加上线程锁(互斥锁),每次执行先获取锁,执行完释放锁;但是加上锁后其实就不是多线程执行了,而是变成串行(一个一个的执行),在所中间加上sleep()就可以看出效果

    import threading,time
    
    num = 0
    lock = threading.Lock()
    def run():
        lock.acquire()  #获取锁
        global num #声明全局变量
        time.sleep(1)
        num += 1
        print(num,time.time())
        lock.release()  #释放锁
    
    for i in range(10):
        t = threading.Thread(target=run)
        t.start()
    
    print(num)
    

      运行结果:

    0
    1 1542440306.993258
    2 1542440307.99377
    3 1542440308.9952087
    4 1542440309.9967873
    5 1542440310.9977813
    6 1542440311.9988816
    7 1542440312.9992244
    8 1542440314.000361
    9 1542440315.001824
    10 1542440316.002374
    
    Process finished with exit code 0
    

     4.递归锁

    threading.RLock()为递归锁,当添加多个锁的时候就应该用递归锁,不然程序会锁死,成为死循环

    import threading
    
    def run1():
        print("grab the first part data")
        rlock.acquire() #第二次加锁 内层锁
        global num
        num += 1
        rlock.release()  #释放内层锁
        return num
    
    def run2():
        print("grab the second part data")
        rlock.acquire()
        global num2
        num2 += 1
        rlock.release()
        return num2
    
    def run3():
        rlock.acquire() #第一次加锁, 最外层锁
        res = run1()
        print('--------between run1 and run2-----')
        res2 = run2()
        rlock.release() #释放最外层锁
        print(res, res2)
    
    if __name__ == '__main__':
        num, num2 = 0, 0
        rlock = threading.RLock()
        for i in range(5):
            t = threading.Thread(target=run3)
            t.start()
    
    while threading.active_count() != 1:  #判断子线程是否执行完
        print(threading.active_count())  #递归锁
    else:
        print('----all threads done---')
        print(num, num2)
    

      运行结果:

    grab the first part data
    --------between run1 and run2-----
    grab the second part data
    1 1
    grab the first part data
    --------between run1 and run2-----
    grab the second part data
    2 2
    grab the first part data2
    2
    2
    --------between run1 and run2-----
    grab the second part data
    3 3
    
    ----all threads done---
    3 3
    
    Process finished with exit code 0
    

      5.信息量

    互斥锁 同时只允许一个线程更改数据,而Semaphore是同时允许一定数量的线程更改数据 ,比如厕所有3个坑,那最多只允许3个人上厕所,后面的人只能等里面有人出来了才能再进去,从运行结果来看5个线程同时运行的。

    import threading,time
    
    num = 0
    semaphore = threading.BoundedSemaphore(5)  #信息量, 同时最多允许5个线程
    def run():
        semaphore.acquire()  #获取信息量
        global num #声明全局变量
        time.sleep(1)
        num += 1
        print(num,time.time())
        semaphore.release()  #释放信息量
    
    for i in range(20):
        t = threading.Thread(target=run)
        t.start()
    
    print(num)
    

      运行结果:

    0
    1 1542440702.8448975
    2 1542440702.845902
    3 1542440702.845902
    4 1542440702.845902
    5 1542440702.8469012
    
    6 1542440703.8459327
    7 1542440703.846936
    8 1542440703.846936
    9 1542440703.847937
    10 1542440703.847937
    
    11 1542440704.847123
    12 1542440704.848123
    13 1542440704.848123 
    14 1542440704.8491254
    15 1542440704.8491254
    
    16 1542440705.8484023
    17 1542440705.8494039
    18 1542440705.8494039
    19 1542440705.8504024
    20 1542440705.8504024
    
    Process finished with exit code 0
    

      6.python自带的队列 Queue()  LifoQueue()  PriorityQueue()

    import queue
    ''' python自带的队列  '''
    q = queue.Queue() #先入先出
    lq = queue.LifoQueue() #后进先出  (用法同Queue())
    
    q.put(1,block=True,timeout=1)  #block=True 没有数据会锁死,False不会锁死, timeout 超时时间
    q.put(2)
    q.put(3)
    print("Queue()大小:", q.qsize())  # 队列中的大小
    for i in range(3):
        q1 = q.get(block=True,timeout=2)  #block=True 没有数据会锁死,False不会锁死, timeout 超时时间
        print(q1)
    
    pq = queue.PriorityQueue()  #根据优先级进出
    pq.put(5,'jiad')
    pq.put(-2,'ieamd')
    pq.put(9,'bmoad')
    print("PriorityQueue()大小:", pq.qsize())  # 队列中的大小
    for i in range(3):
        pq1 = pq.get(block=True,timeout=2)  #block=True 没有数据会锁死,False不会锁死, timeout 超时时间
        print(pq1)
    

      运行结果:

    Queue()大小: 3
    1
    2
    3
    PriorityQueue()大小: 3
    -2
    5
    9
    

      通过队列实现生产者消费者关系

    import threading,time,queue
    
    q = queue.Queue(maxsize=10)
    
    def Producer(name):
        '''  生产者 '''
        count = 1
        while True:
            q.put("酱骨头%s"%count)
            print("已经生产的数量:",count)
            time.sleep(2)
            count +=1
    
    def Consumer(name):
        while True:
            while q.qsize()>0:
                print("%s取到了%s,并且吃了它"%(name,q.get()))
                time.sleep(1)
    
    p = threading.Thread(target=Producer,args=("张三",))
    c1 = threading.Thread(target=Consumer,args=("李四",))
    c2 = threading.Thread(target=Consumer,args=("王五",))
    p.start()
    c1.start()
    c2.start()
    

      7.event 事件,可以设置标志位(event.set()),清空标识位(event.clear()),等待(event.wait()) 是否存在标识位event.is_set()

    import threading,time
    
    event = threading.Event()
    def lighter():
        ''' 模拟红绿灯 10秒钟绿灯,10秒钟红灯 有标识位表示绿灯,没有表示红灯'''
        count = 0  # 时间长度
        event.set()  # 设置标识位
        while True:
            if count>10 and count<=20: #红灯
                event.clear()  # 把标志位清了
                print("33[31;1m红灯.STOP...33[0m")
            elif count >20 :
                event.set()  # 设置标志位
                count = 0  #清零
            else:
                print("33[36;1m绿灯.RUN...33[0m")
            time.sleep(1)
            count += 1
    
    def car(name):
        ''' 汽车的线程 '''
        while True:
            if event.is_set(): #存在标识位
                print("%s is run..."%name)
                time.sleep(1)
            else:
                print("红灯需要等待...")
                event.wait()
    
    lt = threading.Thread(target=lighter,)
    lt.start() #红绿灯的线程
    
    ct = threading.Thread(target=car,args=("Tesla",))
    ct.start() #汽车的线程
    

      

     

      

  • 相关阅读:
    相对路径与绝对路径问题
    javaee自定义servlet的步骤
    Struts1.X与Spring集成——另外一种方案
    菜鸟也能学cocos2dx3.0 浅析刀塔传奇(下)
    JAVA之了解类载入器Classloader
    IOS 编程中引用第三方的方类库的方法及常见问题
    通过eclipse的egit插件提交提示Auth fail
    定时器0的方式1 定时器1的方式1 数码管和led
    MongoDB入门学习(四):MongoDB的索引
    J2EE--JDBC
  • 原文地址:https://www.cnblogs.com/bert227/p/9964524.html
Copyright © 2020-2023  润新知