• 初识python多线程


    参考:

    python3多线程——官方教程中文版

    python多线程-1

    python多线程-2.1

    python多线程-2.2

    python多线程-3

    python多线程-4



    python中的多线程无法在多个核上同时进行,这是因为:

    Python的线程虽然是真正的线程,但解释器执行代码时,有一个GIL锁:Global Interpreter Lock,任何Python线程执行前,必须先获得GIL锁,然后,每执行100条字节码,解释器就自动释放GIL锁,让别的线程有机会执行。这个GIL全局锁实际上把所有线程的执行代码都给上了锁,所以,多线程在Python中只能交替执行,即使100个线程跑在100核CPU上,也只能用到1个核。

    GIL是Python解释器设计的历史遗留问题,通常我们用的解释器是官方实现的CPython,要真正利用多核,除非重写一个不带GIL的解释器。

    所以,在Python中,可以使用多线程,但不要指望能有效利用多核。如果一定要通过多线程利用多核,那只能通过C扩展来实现,不过这样就失去了Python简单易用的特点。

    不过,也不用过于担心,Python虽然不能利用多线程实现多核任务,但可以通过多进程实现多核任务。多个Python进程有各自独立的GIL锁,互不影响。


    Thread类构造方法

    Thread(group = None, target = None, name = None, args = (), kwargs = {})

    • group: 线程组,目前尚未实现,必须是None。

    • target:要执行的方法。

    • name:线程名。(默认是Thread-number)

    • args/kwargs:要传入方法的参数。

    实例方法

    • isAlive():返回线程是否在运行。

    • get/setName:获取/设置线程名。

    • start():启动线程

    • is/setDaemon(bool):获取/设置是后台进程(默认是前台进程(False))。

      • 如果是后台进程,主线程执行过程中,后台线程也在进行,主线程执行完毕之后,后台线程不论成功与否,主线程和后台线程均停止。

      • 如果是前台线程,主线程执行过程中,前台线程也在执行,主线程执行完毕后,等待前台线程执行完成后,程序停止。

    • join():阻塞当前上下文环境的线程,知道调用此方法的线程终止或者到达指定的timeout(可选参数)。

    创建线程的两种方法

    • 方法1:将想要执行的方法传给Thread类中的构造方法。

    • 方法2:定义一个新类继承Thread类,重写run方法。

    # 创建线程的两种方法
    
    # 第一种:将要执行的方法传给Thread的构造方法
    import threading
    import time
    
    def run(n):
        time.sleep(1)
        print(n)
    
    for i in range(5):
        t = threading.Thread(target = run, args = (i,))
        t.start()
    
    
    # 第二种,继承Thread类,重写run()方法
    
    class Mythread(threading.Thread):
        def __init__(self, arg):
            threading.Thread.__init__(self)
            # super().__init__(self)
            self.arg = arg
    
        def run(self):
            time.sleep(1)
            print(self.arg)
    
    for i in range(5,10):
        t = Mythread(i)
        t.start()
    

    setDeamon方法

    设置是后台进程(True)还是前台进程(False)。(默认是False)

    例题如下:

    #!/usr/bin/env python
    # -*- coding:utf-8 -*-
    import threading
    import time
    
    
    class Mythread(threading.Thread):
        def __init__(self, arg, name):
            threading.Thread.__init__(self, name=name)
            # super().__init__(self)
            self.arg = arg
    
        def run(self):
            time.sleep(3)
            print(self.arg)
    
    
    thread = Mythread(10, 'TestThread')
    
    # thread.setDaemon(True)
    thread.start()
    
    for i in range(2):
        print(threading.current_thread().name + ': ', i)
        # 判断子线程是不是存活
        print(thread.name + ': ', thread.isAlive())
        time.sleep(1)
    

    输出:

    MainThread:  0
    TestThread:  True
    MainThread:  1
    TestThread:  True
    10
    

    在线程启动前,设置:

    thread.setDaemon(True)
    

    输出:

    MainThread:  0
    TestThread:  True
    MainThread:  1
    TestThread:  True
    

    可以看到,子线程并没有完全进行完毕,因为这时主线程已经结束了,子线程也要跟着结束。

    join方法

    阻塞线程上下文,直到调用此方法的线程终止或到达指定的timeout,即使设置了setDeamon(True)的主线程也要等待子线程结束。

    实例如下:

    #!/usr/bin/env python
    # -*- coding:utf-8 -*-
    import threading
    import time
    
    
    class Mythread(threading.Thread):
        def __init__(self, arg, name):
            threading.Thread.__init__(self, name=name)
            # super().__init__(self)
            self.arg = arg
    
        def run(self):
            time.sleep(1)
            print(self.name + ': ', self.arg)
    
    
    thread = Mythread(10, 'TestThread')
    
    for i in range(5):
        t = Mythread(i, 'Thread' + str(i))
        t.start()
        # t.join()
    
    
    输出(比较乱,因为停顿1s后,所有的线程都在准备就绪的状态):
    
    ```python
    Thread2:  2
    Thread4:  Thread1: Thread0:  0Thread3:  3
    
     1
    4
    

    我们在每个线程的后面,用join方法进行阻塞,如下:

    t.join()
    

    输出:

    Thread0:  0
    Thread1:  1
    Thread2:  2
    Thread3:  3
    Thread4:  4
    

    Lock类、Rlock类

    由于线程之间的随机调度:某个线程可能在执行n条后,CPU接着执行其他线程。为了防止多个线程同时操作一个内存资源时造成混乱,我们使用了锁。

    例如,我们同时使用两个线程操作一个全局变量,如下:

    #!/usr/bin/env python
    # -*- coding:utf-8 -*-
    import threading
    
    
    
    class Mythread(threading.Thread):
        def __init__(self, name, i):
            threading.Thread.__init__(self, name=name)
            self.i = i
    
        def run(self):
    
            global count
            for i in range(100000):
                count -= self.i
                count += self.i
    
    
    count = 10
    
    t1 = Mythread('Thread' + str(1) + ': ', 100)
    t2 = Mythread('Thread' + str(2) + ': ', 50)
    t1.start()
    t2.start()
    t1.join()
    t2.join()
    
    print('count = ', count)
    

    在运行次数比较少的时候,可以输出正确答案,但是当循环次数变大之后,每次输出的结果就不一样了。

    但是,当我们使用了Lock类之后,如下:

    #!/usr/bin/env python
    # -*- coding:utf-8 -*-
    import threading
    import time
    
    
    class Mythread(threading.Thread):
        def __init__(self, name, i):
            threading.Thread.__init__(self, name=name)
            self.i = i
    
        def run(self):
    
            global count
            for i in range(100000):
                lock.acquire()    # 获取锁
                count -= self.i
                count += self.i
                lock.release()    # 释放锁
    
    
    count = 10
    lock = threading.Lock()
    t1 = Mythread('Thread' + str(1) + ': ', 100)
    t2 = Mythread('Thread' + str(2) + ': ', 50)
    t1.start()
    t2.start()
    t1.join()
    t2.join()
    
    print('count = ', count)
    

    Lock类与RLock类的区别。

    这两种锁的主要区别是:在同一线程内,对RLock进行多次acquire()操作,程序不会阻塞。

    • threading.Lock() 加载线程的锁对象,是一个基本的锁对象,一次只能一个锁定,其余锁请求,需等待锁释放后才能获取

    • threading.RLock() 多重锁,在同一线程中可用被多次acquire。 如果使用RLock,那么acquire和release必须成对出现。

  • 相关阅读:
    堆排序,C++模板编程
    洗牌程序的两种实现方法比较
    读取/保存xml文件的类(序列化/反序列化)
    [返回上一页,并且刷新]
    实现等级的存储过程sql
    C#实现WMI读取远程计算机信息【原】
    开源IT资产管理系统>OCS Inventory NG服务端
    xaf实现自定义只读参数
    How to show a Detail View via code
    15个最好的免费开源电子商务平台
  • 原文地址:https://www.cnblogs.com/anzhengyu/p/11430132.html
Copyright © 2020-2023  润新知