zoukankan      html  css  js  c++  java
  • Python-多线程详解

    一、介绍线程

    1)什么是线程?

    线程(英语:thread)是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。在Unix System V及SunOS中也被称为轻量进程(lightweight processes),但轻量进程更多指内核线程(kernel thread),而把用户线程(user thread)称为线程

    2)为什么使用线程

     线程在程序中是独立的、并发的执行流。与分隔的进程相比,进程中线程之间的隔离程度要小,它们共享内存、文件句柄 和其他进程应有的状态。 因为线程的划分尺度小于进程,使得多线程程序的并发性高。进程在执行过程之中拥有独立的内存单元,而多个线程共享 内存,从而极大的提升了程序的运行效率。 线程比进程具有更高的性能,这是由于同一个进程中的线程都有共性,多个线程共享一个进程的虚拟空间。线程的共享环境 包括进程代码段、进程的共有数据等,利用这些共享的数据,线程之间很容易实现通信。 操作系统在创建进程时,必须为改进程分配独立的内存空间,并分配大量的相关资源,但创建线程则简单得多。因此,使用多线程 来实现并发比使用多进程的性能高得要多。

    3)线程的优点

     总结起来,使用多线程编程具有如下几个优点: 进程之间不能共享内存,但线程之间共享内存非常容易。 操作系统在创建进程时,需要为该进程重新分配系统资源,但创建线程的代价则小得多。因此使用多线程来实现多任务并发执行比使用多进程的效率高 python语言内置了多线程功能支持,而不是单纯地作为底层操作系统的调度方式,从而简化了python的多线程编程。

    二、实现方式

    导包

    import threading
    import time
    from threading import Lock, Thread

    1)普通创建多线程方式

    import threading
    import time
    
    def run(n):
        print('task',n)
        time.sleep(1)
        print('2s')
        time.sleep(1)
        print('1s')
        time.sleep(1)
        print('0s')
        time.sleep(1)
    
    if __name__ == '__main__':
        t1 = threading.Thread(target=run,args=('t1',))     # target是要执行的函数名(不是函数),args是函数对应的参数,以元组的形式存在
        t2 = threading.Thread(target=run,args=('t2',))
        t1.start()
        t2.start()

    2)自定义线程

    继承threading.Thread来定义线程类,其本质是重构Thread类中的run方法

    class MyThread(threading.Thread):
        def __init__(self,n):
            super(MyThread,self).__init__()   #重构run函数必须写
            self.n = n
    
        def run(self):
            print('task',self.n)
            time.sleep(1)
            print('2s')
            time.sleep(1)
            print('1s')
            time.sleep(1)
            print('0s')
            time.sleep(1)
    
    if __name__ == '__main__':
        t1 = MyThread('t1')
        t2 = MyThread('t2')
        t1.start()
        t2.start()

    3)守护线程

    下面这个例子,这里使用setDaemon(True)把所有的子线程都变成了主线程的守护线程, 因此当主线程结束后,子线程也会随之结束,所以当主线程结束后,整个程序就退出了。 所谓’线程守护’,就是主线程不管该线程的执行情况,只要是其他子线程结束且主线程执行完毕,主线程都会关闭。也就是说:主线程不等待该守护线程的执行完再去关闭。

    def run(n):
        print('task',n)
        time.sleep(1)
        print('3s')
        time.sleep(1)
        print('2s')
        time.sleep(1)
        print('1s')
    
    if __name__ == '__main__':
        t=threading.Thread(target=run,args=('t1',))
        t.setDaemon(True)
        t.start()
        print('end')

    注:通过执行结果可以看出,设置守护线程之后,当主线程结束时,子线程也将立即结束,不再执行

    4)主线程等待子线程结束

    为了让守护线程执行结束之后,主线程再结束,我们可以使用join方法,让主线程等待子线程执行

    def run(n):
        print('task',n)
        time.sleep(2)
        print('5s')
        time.sleep(2)
        print('3s')
        time.sleep(2)
        print('1s')
    if __name__ == '__main__':
        t=threading.Thread(target=run,args=('t1',))
        t.setDaemon(True)    #把子线程设置为守护线程,必须在start()之前设置
        t.start()
        t.join()     #设置主线程等待子线程结束
        print('end')

     5)多线程共享全局变量

    线程时进程的执行单元,进程时系统分配资源的最小执行单位,所以在同一个进程中的多线程是共享资源的

    g_num = 100
    def work1():
        global  g_num
        for i in range(3):
            g_num+=1
        print('in work1 g_num is : %d' % g_num)
    
    def work2():
        global g_num
        print('in work2 g_num is : %d' % g_num)
    
    if __name__ == '__main__':
        t1 = threading.Thread(target=work1)
        t1.start()
        time.sleep(1)
        t2=threading.Thread(target=work2)
        t2.start()

     6)互斥锁

    由于线程之间是进行随机调度,并且每个线程可能只执行n条执行之后,当多个线程同时修改同一条数据时可能会出现脏数据, 所以出现了线程锁,即同一时刻允许一个线程执行操作。线程锁用于锁定资源,可以定义多个锁,像下面的代码,当需要独占 某一个资源时,任何一个锁都可以锁定这个资源,就好比你用不同的锁都可以把这个相同的门锁住一样。 由于线程之间是进行随机调度的,如果有多个线程同时操作一个对象,如果没有很好地保护该对象,会造成程序结果的不可预期, 我们因此也称为“线程不安全”。

    def work():
        global n
        lock.acquire()      # 加锁
        temp = n
        time.sleep(0.1)
        n = temp-1
        print n
        lock.release()      # 解锁
    
    
    if __name__ == '__main__':
        lock = Lock()
        n = 100
        l = []
        # 创建 100 个线程
        for i in range(100):
            p = Thread(target=work)
            l.append(p)
            p.start()
        for p in l:
            p.join()

    7)递归锁

    RLcok类的用法和Lock类一模一样,但它支持嵌套,在多个锁没有释放的时候一般会使用RLock类

    def func(lock):
        global gl_num
        lock.acquire()
        gl_num += 1
        time.sleep(1)
        print(gl_num)
        lock.release()
    
    
    if __name__ == '__main__':
        gl_num = 0
        lock = threading.RLock()
        for i in range(10):
            t = threading.Thread(target=func,args=(lock,))
            t.start()

    8)信号量(BoundedSemaphore类)

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

    def run(n,semaphore):
        semaphore.acquire()   #加锁
        time.sleep(3)
        print('run the thread:%s
    ' % n)
        semaphore.release()    #释放
    
    
    if __name__== '__main__':
        num=0
        semaphore = threading.BoundedSemaphore(5)   #最多允许5个线程同时运行
        for i in range(22):
            t = threading.Thread(target=run,args=('t-%s' % i,semaphore))
            t.start()
        while threading.active_count() !=1:
            pass
        else:
            print('----------all threads done-----------')
  • 相关阅读:
    webpack基础
    LeetCode232. 用栈实现队列做题笔记
    mysql 时间加减一个月
    leetcode 1381. 设计一个支持增量操作的栈 思路与算法
    LeetCode 141. 环形链表 做题笔记
    leetcode 707. 设计链表 做题笔记
    leetcode 876. 链表的中间结点 做题笔记
    leetcode 143. 重排链表 做题笔记
    leetcode 1365. 有多少小于当前数字的数字 做题笔记
    LeetCode1360. 日期之间隔几天 做题笔记
  • 原文地址:https://www.cnblogs.com/shangwei/p/14304282.html
Copyright © 2011-2022 走看看