Python 多线程-守护线程、线程等待、互斥锁、信号量详细解读!

目录

Python 多线程详解

一、线程

  • 概念

  • 多线程优势

二、Python 线程

  • threading

  • 重新写run方法

  • 守护线程 setDaemon(True)

  • 线程等待 join( )

  • 多线程共享全局变量

  • 互斥锁 Lock( )

  • 信号量 (BoundedSemaphore类)

一、线程

1. 概念

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

2. 多线程优势

  1. 多任务执行。

一个进程中可以并发多个线程,每条线程并行执行不同的任务

  1. 共享资源。

在同一进程中的各个线程,都可以共享该进程所拥有的资源,这首先表现在:所有线程都具有相同的地址空间(进程的地址空间)。

二、Python 线程

1. threading

线程创建,需要导入Python内置模块threading中的thread()方法创建线程。

例如:

import threading
from time import sleep


def dance():
    for i in range(10):
        print("我正在跳舞%s"%i)
        sleep(1)


t1 = threading.Thread(target=dance)
t2 = threading.Thread(target=dance)
t1.start()

run:

我正在跳舞0
我正在跳舞0
我正在跳舞1
我正在跳舞1
我正在跳舞2
我正在跳舞2
我正在跳舞3我正在跳舞3
我正在跳舞4
我正在跳舞4
我正在跳舞5我正在跳舞5
我正在跳舞6
我正在跳舞6
我正在跳舞7
我正在跳舞7
我正在跳舞8
我正在跳舞8
我正在跳舞9我正在跳舞9

2. 重新写run方法

在threading.Thread类里面有一个run()方法,当我们调用Thread的时候,程序会自动调用Thread里面的run()方法。所以,重写run方法,需要继承threading.Thread类,再对run()进行重写。

import threading
from time import sleep


class SelfThread(threading.Thread):

    def run(self):
        self.dance()

    def dance(self):
        for i in range(3):
            print("我正在跳舞%s"%i)
            sleep(1)


t1 = SelfThread()
t2 = SelfThread()
t1.start()
t2.start()

run:

我正在跳舞0
我正在跳舞0
我正在跳舞1
我正在跳舞1
我正在跳舞2
我正在跳舞2

3. 守护线程 setDaemon(True)

守护线程,顾名思义就是守护主线程的,当主线程结束之前守护线程不能够提前结束,主线程一旦结束守护线程也随之结束了。

import threading
from time import sleep


class SelfThread(threading.Thread):

    def run(self):
        self.dance()

    def dance(self):
        for i in range(3):
            print("我正在跳舞%s"%i)
            sleep(1)


t1 = SelfThread()
# 设置t1为守护线程
t1.setDaemon(True)
t1.start()
print("主线程结束...")

主线程:主线程,当一个程序启动时,就有一个进程被操作系统创建,与此同时一个线程也立刻运行,该线程通常叫做程序的主线程。主线程的重要性体现在两方面:1.是产生其他子线程的线程;2.通常它必须最后完成执行比如执行各种关闭动作。

run:

我正在跳舞0主线程结束...


Process finished with exit code 0

4. 线程等待 join( )

在上面的代码中,守护线程并没有执行完毕,那么想要守护线程在主线程结束后等待守护线程执行完毕,则需要使用join()方法。

import threading
from time import sleep


class SelfThread(threading.Thread):

    def run(self):
        self.dance()

    def dance(self):
        for i in range(3):
            print("我正在跳舞%s"%i)
            sleep(1)


t1 = SelfThread()
# 设置t1为守护线程
t1.setDaemon(True)
t1.start()
# join() 等待所有线程(包括守护线程)结束,主线程才结束。
t1.join()
print("主线程结束...")

run:

我正在跳舞0
我正在跳舞1
我正在跳舞2
主线程结束...

Process finished with exit code 0

5. 多线程共享全局变量

进程是系统分配资源的最小单位,线程是计算机最小的执行单位。所以一个进程里面的所有线程的资源都是共享的,比如多个线程共同修改一个列表,字典,元组或一个number。

class SelfThread(threading.Thread):

    def __init__(self, self_list, n):
        super().__init__()
        self.self_list = self_list
        self.n = n

    def run(self):
        self.add_list()

    def add_list(self):
        for i in range(3):
            self_list.append("线程%s"%(self.n))
            sleep(1)


self_list = []
t1 = SelfThread(self_list,1)
t2 = SelfThread(self_list,2)
# 设置t1为守护线程
t1.start()
t2.start()
# join() 等待所有线程(包括守护线程)结束,主线程才结束。
t1.join()
t2.join()
print("主线程结束...")
# t1,t2线程总共会在self_list中,添加6个元素。说明两个线程都是修改同一个列表。
print(self_list)

run:

主线程结束...
['线程1', '线程2', '线程1', '线程2', '线程1', '线程2']

Process finished with exit code 0

t1,t2线程总共会在self_list中,添加6个元素。说明两个线程都是修改同一个列表。

6. 互斥锁 Lock( )

我们都知道线程之间是随机调度的,当多个线程同时修改一条数据的时候可能会导致数据修改的逻辑混乱。所以,互斥锁应运而生,我们只需要保证同一个时刻,只允许一个线程对同一个对象进行操作,线程操作完毕后,才允许其他线程进行修改添加等其他操作,这就是互斥锁。

下面以修改某个数字为例:目的是创建3个线程,每个线程只对Num-1 这样一次操作,所有线程执行完毕后,num应该为97,而最终的结果是99,这就是没有加互斥锁的原因。

  1. 错误的示范:

代码:

import time


def change_num():
    global num
    number=num
    time.sleep(0.1)
    num=number-1


if __name__ == '__main__':
    lock=Lock()
    num=100
    Threads=[]
    for i in range(3):
        p=Thread(target=change_num)
        Threads.append(p)
        p.start()
    for p in Threads:
        p.join()

    print(num)

run:

99

Process finished with exit code 0

分析原因:
Python 多线程-守护线程、线程等待、互斥锁、信号量详细解读!_第1张图片

正确的示范:

from threading import Thread,Lock
import time


def change_num():
    global num
    
    lock.acquire()
    number=num
    time.sleep(0.1)
    num=number-1
    lock.release()


if __name__ == '__main__':
    lock=Lock()
    num=100
    Threads=[]
    for i in range(3):
        p=Thread(target=change_num)
        Threads.append(p)
        p.start()
    for p in Threads:
        p.join()

    print(num)

run:

97

Process finished with exit code 0

7. 信号量 (BoundedSemaphore类)

互斥锁,同一时刻只能允许一个线程修改同一个对象,那信号量可以设置同一时刻N个线程去修改。就好比互斥锁,原来厕所里只有一个蹲坑,一次只能一个人使用;通过控制信号量,新增2个蹲坑后,现在可以3个人同时上厕所。

代码:

from threading import Thread,Lock
from time import sleep


def dance(t, semaphore):
    for i in range(3):
        semaphore.acquire()
        print("我正在跳舞%s"%(t))
        sleep(1)
        semaphore.release()


if __name__ == '__main__':
    lock=Lock()
    semaphore = threading.BoundedSemaphore(2)  # 最多允许2个线程同时运行
    Threads=[]
    for i in range(100):
        p=Thread(target=dance, args=("t-%s" % i, semaphore))
        Threads.append(p)
        p.start()
    for p in Threads:
        p.join()

run:

t-0 正在跳舞 0
t-1 正在跳舞 0
t-0 正在跳舞 1
t-1 正在跳舞 1
t-1 正在跳舞 2t-0 正在跳舞 2

t-7 正在跳舞 0
t-6 正在跳舞 0
t-7 正在跳舞 1t-6 正在跳舞 1

t-99

更多关于python多线程的知识请点击【官方文档】链接:https://docs.python.org/zh-tw/3.7/library/threading.html#timer-objects

你可能感兴趣的:(Python,多线程,python,thread)