初识多线程--python

本文详细介绍Python中的线程概念,包括线程的基本操作如创建、启动、等待和停止,以及线程间的同步机制如锁、条件变量和事件。通过具体实例演示了如何在Python程序中有效地管理和利用线程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言:

python也有线程的概念,原理和操作系统的线程一样,只是语法什么的不一样。

 

1- 线程相关接口

python线程编程需要导入模块threading。先了解下如何获取线程名字、活动线程个数、主线程。

#!/usr/bin/python
import threading

#获取当前线程对象
cur_thread = threading.current_thread()
print("cur_thread name:{0}".format(cur_thread.name))

#获取活动线程个数
print("active thread cnt:{0}".format(threading.active_count()))

#获取主线程名字
main_thead = threading.main_thread()
print("main_thread name:{0}".format(main_thead.name))

 

2- 创建线程(threading类)

使用threading.Thread方法创建线程,线程创建后执行start,线程才会运行。threading.Thread参数如下:

threading.Thread(group=None, target=None, name=None, args=(), kwargs={}, *, daemon=None)

其中常用的有target,用于指定线程入口;name用于指定线程名字;args和kwargs用于给线程传递参数,暂不关心

注意:我们创建线程入口里面一般是个死循环,永远不返回。死循环里做监控、定期执行、条件执行等的任务

 

示例:

#!/usr/bin/python
import threading
import time

def thread_entry():
    while True:
        cur_thread = threading.current_thread()
        print("cur_thread name:{0}".format(cur_thread.name))
        time.sleep(1)


def main():
    thread1 = threading.Thread(target=thread_entry, name="thread1")
    thread1.start()

if __name__ == "__main__":
    main()


 

3- 创建线程(使用类)

3.1- 使用类创建线程

除了借助thread.Thread创建对象实体,我们还可以自己写个类继承threading.Thread。

示例:

示例中执行thread1.start()会跳转到Mythread.run接口,即run接口重写了start()。为何会这样呢?

#!/usr/bin/python
import threading
import time

class MyThread(threading.Thread):
    def __init__(self, name):
        super(MyThread, self).__init__(name=name)

    #重写threading.Thread.start()接口
    def run(self):
        cur_thread = threading.current_thread()
        while True:
            print("cur_thread name:{0}".format(cur_thread.name))
            time.sleep(1)
    
def main():    
    thread1 = MyThread("thread-1")
    thread1.start()

if __name__ == "__main__":
    main()


3.2- 创建多个线程如何指定入口?

另外如果我们创建多个线程,如何为每个线程单独指定入口呢?

可以使用下面两种方法,当然应该还有别的方式,欢迎跟帖留言

一种是我们在创建线程对象时,传入线程入口thread_entry(),然后在run接口中self.thread_entry()

另一种时在run中判定线程名字,根据名字执行不同入口。

这里我们偷下懒,让线程1、2入口名字一样。。。。

 

F1:

#!/usr/bin/python
import threading
import time

class MyThread(threading.Thread):
    def __init__(self, target, name):
        super(MyThread, self).__init__(name=name)
        self.target = target

    # 重写threading.Thread.start()接口
    def run(self):
        self.target()

def thread_entry():
    while True:
        cur_thread = threading.current_thread()
        print("cur_thread name:{0}".format(cur_thread.name))
        time.sleep(1)

def main():
    thread1 = MyThread(thread_entry, "thread-1")
    thread1.start()
    thread2 = MyThread(thread_entry, "thread-2")
    thread2.start()


if __name__ == "__main__":
    main()

 

F2:

#!/usr/bin/python
import threading
import time

class MyThread(threading.Thread):
    def __init__(self, target, name):
        super(MyThread, self).__init__(name=name)

    #重写threading.Thread.start()接口
    def run(self):
        cur_thread = threading.current_thread()
        if cur_thread.name == "thread-1":
            thread_entry()
        else:
            thread_entry()

def thread_entry():
    while True:
        cur_thread = threading.current_thread()
        print("cur_thread name:{0}".format(cur_thread.name))
        time.sleep(1)

def main():
    thread1 = MyThread(thread_entry, "thread-1")
    thread1.start()
    thread2 = MyThread(thread_entry, "thread-2")
    thread2.start()

if __name__ == "__main__":
    main()


 

4- join() 等待线程结束

和linux API接口一样,等待线程结束的接口也是join()。当线程1等待线程2结束,线程2返回前,线程1一直是阻塞状态。只有线程2返回,线程1才能继续执行。

应用场景:某个线程需要等待某些事情结束才能继续执行。

举个实际生活中的例子:实际开发过程中,我们要做一个需求:让手机拍照美颜更好看。首先需要市场调研清楚用户需求,调研好后传达给项目。项目才能根据用户反馈做下需求设计。这样需求设计就在市场调研完成后才能开展。

 

示例:

#!/usr/bin/python
import threading
import time

class MyThread(threading.Thread):
    def __init__(self, target, name):
        super(MyThread, self).__init__(name=name)
        self.target = target

    # 重写threading.Thread.start()接口
    def run(self):
        self.target()

def thread_entry():
    cur_thread = threading.current_thread()
    for arg in range(5):
        print("cur_thread name:{0}, run_cnt:{1}".format(cur_thread.name,arg))
        arg += 1
        time.sleep(1)
    print("cur_thread name:{0} is exit".format(cur_thread.name))

def main():
    cur_thread = threading.current_thread()
    print("cur_thread name:{0} is run".format(cur_thread.name))
    thread1 = MyThread(thread_entry, "thread-1")
    thread1.start()
    thread1.join()
    print("cur_thread name:{0} is exit".format(cur_thread.name))


if __name__ == "__main__":
    main()

打印为:

cur_thread name:MainThread is run
cur_thread name:thread-1, run_cnt:0
cur_thread name:thread-1, run_cnt:1
cur_thread name:thread-1, run_cnt:2
cur_thread name:thread-1, run_cnt:3
cur_thread name:thread-1, run_cnt:4
cur_thread name:thread-1 is exit
cur_thread name:MainThread is exit

可以去掉thread1.join对比下不同

 

5- 线程停止

线程一般为死循环,但是我们如果想暂停线程咋办,可以使用下面方式。

创建一个线程thread1是个死循环。当全局变量thread_is_run为真时1s打印一次运行,为假时打印停止。

线程thread2用于控制thread_is_run,间接控制了thread1暂停和运行.

 

注意:我们这里使用time.sleep()来模拟线程1停止,实际上线程1还在运行,只是打印变慢。python应该也提供了线程挂起的接口把。先不关注

 

示例:

#!/usr/bin/python
import threading
import time

class MyThread(threading.Thread):
    def __init__(self, target, name):
        super(MyThread, self).__init__(name=name)
        self.target = target

    # 重写threading.Thread.start()接口
    def run(self):
        self.target()

thread_is_run = True

def thread1_entry():
    global thread_is_run
    cur_thread = threading.current_thread()
    while True:
        if thread_is_run:
            print("cur_thread name:{0} run...".format(cur_thread.name))
            time.sleep(1)
        else:
            print("cur_thread name:{0} stoped".format(cur_thread.name))
            time.sleep(500)
    print("cur_thread name:{0} is exit".format(cur_thread.name))

def thread2_entry():
    cur_thread = threading.current_thread()
    while True:
        global thread_is_run
        cmd = input()
        if cmd == "1":
            thread_is_run = True
            print("input:{0}".format(cmd))
        else:
            thread_is_run = False
            print("input:{0}".format(cmd))
    print("cur_thread name:{0} is exit".format(cur_thread.name))

def main():
    thread1 = MyThread(thread1_entry, "thread-1")
    thread1.start()
    thread2 = MyThread(thread2_entry, "thread-2")
    thread2.start()

if __name__ == "__main__":
    main()

 

6- 线程锁 threading.Lock()

对于多线程问题,经常存在公共资源(临界资源)同时被多个线程访问、修改的问题,如果不能保证多线程互斥地访问临界资源,就存在逻辑上的问题。操作系统一般提供互斥锁、原子锁等。python了解的不多,只知道线程锁threading.lock。

python中的线程锁:当一个线程获取到锁后,其他线程就会阻塞,知道该线程释放锁。

 

示例:

示例中以停车场为背景,停车场共5个车位,初始情况下有还剩3个车位。创建两个线程,一个用于进入停车场停车,一个用于离开停车场。

如果不加锁,可能存在如下情况:

本来停车场还有3个车位,同一时刻A离开停车场,B进入停车场。他们被告知此时还有3个车位。

A离开的快,离开后车位还有3+1=4个

B停车慢,停好车后车位还有3-1=2个

加了锁后就不会出现这种情况。加锁后就相当于只有一个进出口,同一时刻只能进或只能出,这样操作临界资源(停车位数目)就安全了。

#!/usr/bin/python
import random
import threading
import time


class Parking(threading.Thread):
    def __init__(self, target, name):
        super(Parking, self).__init__(name=name)
        self.target = target

    # 重写threading.Thread.start()接口
    def run(self):
        self.target()


lock = threading.Lock()
park_cnt = 3


def drive_in():
    lock.acquire()
    global park_cnt
    if park_cnt == 0:
        print("sorry, you can't drive in, because there is not parking")
    elif park_cnt <= 5:
        park_cnt -= 1
        print("drive in, par_cnt:{0}".format(park_cnt))
    lock.release()
    time.sleep(1)


def drive_out():
    lock.acquire()
    global park_cnt
    if park_cnt == 5:
        print("sorry, you maybe crazy, there is no car in parking")
    elif park_cnt < 5:
        park_cnt += 1
        print("drive out, par_cnt:{0}".format(park_cnt))
    lock.release()
    time.sleep(1)


def drive_in_thread():
    cur_thread = threading.current_thread()
    while True:
        drive_in()
    print("cur_thread name:{0} is exit".format(cur_thread.name))


def drive_out_thread():
    cur_thread = threading.current_thread()
    while True:
        drive_out()
    print("cur_thread name:{0} is exit".format(cur_thread.name))


def main():
    drive_in = Parking(drive_in_thread, "drive_in")
    drive_in.start()

    drive_out = Parking(drive_out_thread, "drive_out")
    drive_out.start()


if __name__ == "__main__":
    main()

 

7- 线程通知 threading.Condition()

acquire([timeout])/release(): 调用关联的锁的相应方法。 
wait([timeout]): 调用这个方法将使线程进入Condition的等待池等待通知并释放锁。使用前线程必须已获得锁定,否则将抛出异常。 
notify(): 调用这个方法将从等待池挑选一个线程并通知,收到通知的线程将自动调用acquire()尝试获得锁定(进入锁定池);其他线程仍然在等待池中。调用这个方法不会释放锁定。使用前线程必须已获得锁定,否则将抛出异常。 
notifyAll(): 调用这个方法将通知等待池中所有的线程,这些线程都将进入锁定池尝试获得锁定。调用这个方法不会释放锁定。使用前线程必须已获得锁定,否则将抛出异常。

注意:调用condition.wait()时会将线程加入到等待池中,并释放锁!释放锁。这一点没看帖子前一直不知道还有这样的操作。。。

 

参考:https://www.cnblogs.com/zj1111184556/p/4612470.html

 

示例:

还是以停车场为例,停车场共5个车位。初始情况下还剩3个车位。

有车驶入,车位--

有车驶出,车位++

如果停车场满了,车无法进入,等待。当有车驶出,通知车主可以停车了

当停车场空了,无车可驶出,等待。当有车驶入,通知可以驶出。

下面的例子只是说明condition用法,并没有很好的保护临界资源(停车位数量)

#!/usr/bin/python
import random
import threading
import time

class Parking(threading.Thread):
    def __init__(self, target, name):
        super(Parking, self).__init__(name=name)
        self.target = target

    # 重写threading.Thread.start()接口
    def run(self):
        self.target()

condition = threading.Condition()
park_cnt = 3
def drive_in():
    global condition
    condition.acquire()
    global park_cnt    
    
    #没有停车位,等待阻塞
    if park_cnt == 0:
        print("sorry, you can't drive in, because there is not parking")
        condition.wait()
    #有停车位,将车辆驶入停车场。现在停车场有车,所以通知车可以驶出停车场
    park_cnt -= 1
    print("drive in, par_cnt:{0}".format(park_cnt))
    condition.notify()
    condition.release()
    #sleeptime = random.randint(1, 5)
    #time.sleep(sleeptime)
    time.sleep(1)

def drive_out():
    global condition
    condition.acquire()
    global park_cnt
    #停车场没有车,不可驶出,阻塞等待有车才驶出
    if park_cnt == 5:
        print("sorry, there is no car in parking")
        condition.wait()
    #停车场有车,可以驶出。驶出后空出车位,所以通知车可以停入
    park_cnt += 1
    print("drive out, par_cnt:{0}".format(park_cnt))
    condition.notify()
    condition.release()
    sleeptime = random.randint(1, 5)
    time.sleep(sleeptime)

def drive_in_thread():
    cur_thread = threading.current_thread()
    while True:        
        drive_in()        
    print("cur_thread name:{0} is exit".format(cur_thread.name))

def drive_out_thread():
    cur_thread = threading.current_thread()
    while True:        
        drive_out()
    print("cur_thread name:{0} is exit".format(cur_thread.name))

def main():
    drive_in = Parking(drive_in_thread, "drive_in")
    drive_in.start()
    
    drive_out = Parking(drive_out_thread, "drive_out")
    drive_out.start()
        
if __name__ == "__main__":
    main()

 

8- 线程通知threading.Event()

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值