Python3多线程详解

为什么要使用多线程?

使用多线程,可以同时进行多项任务,可以使用户界面更友好,还可以后台执行某些用时长的任务,同时具有易于通信的优点。(对于GIL以及Python多线程对于效率的影响讨论可看知乎[为什么有人说 Python 的多线程是鸡肋呢?

python3中多线程的实现使用了threading模块,它允许同一进程中运行多个线程。

如何创建和执行一个线程

一般我们有两种方法来创建线程,一种是以某个函数来作为起点,另一种是继承Thread类。

方法一

获取一个Thread对象,构造参数中target是起点函数,注意不要加括号。假如起点函数有参数,则可以通过args输入元组参数或者kwargs输入字典参数。

#! -*-conding=: UTF-8 -*-
# 2023/5/6 15:53
import time
from threading import Thread

def task():
    print("另外开始一个子线程做任务啦")
    time.sleep(1)  # 用time.sleep模拟任务耗时
    print("子线程任务结束啦")

if __name__ == '__main__':
    print("这里是主线程")
    # 创建线程对象
    t1 = Thread(target=task)
    # 启动
    t1.start()
    time.sleep(0.3)
    print("主线程依然可以干别的事")

输出结果为:

这里是主线程
另外开始一个子线程做任务啦
主线程依然可以干别的事
子线程任务结束啦

方法二
#! -*-conding=: UTF-8 -*-
# 2023/5/6 15:53
import time
from threading import Thread

class NewThread(Thread):
    def __init__(self):
        Thread.__init__(self)  # 必须步骤

    def run(self):  # 入口是名字为run的方法
        print("开始新的线程做一个任务啦")
        time.sleep(1)  # 用time.sleep模拟任务耗时
        print("这个新线程中的任务结束啦")

if __name__ == '__main__':
    print("这里是主线程")
    # 创建线程对象
    t1 = NewThread()
    # 启动
    t1.start()
    time.sleep(0.3)  # 这里如果主线程结束,子线程会立刻退出,暂时先用sleep规避
    print("主线程依然可以干别的事")

输出结果为:

这里是主线程
开始新的线程做一个任务啦
主线程依然可以干别的事
这个新线程中的任务结束啦

正式介绍threading模块

docs.python.org/3/library/t…

关于线程信息的函数:

  • threading.active_count():返回当前存活的Thread对象数量。
  • threading.current_thread():返回当前线程的Thread对象。
  • threading.enumerate():列表形式返回所有存活的Thread对象。
  • threading.main_thread():返回主Thread对象。

Thread对象的方法及属性:

  • Thread.name:线程的名字,没有语义,可以相同名称。
  • Thread.ident:线程标识符,非零整数。
  • Thread.Daemon:是否为守护线程。
  • Thread.is_alive():是否存活。
  • Thread.start():开始线程活动。若多次调用抛出RuntimeError。
  • Thread.run():用来重载的,
  • Thread.join(timeout=None):等待直到线程正常或异常结束。尚未开始抛出RuntimeError
  • Thread(group=None, target=None, name=None, args=(), kwargs={}, *, deamon=None):构造函数。

守护线程 Daemon

在Python 3中,守护线程(daemon thread)是一种特殊的线程,它在程序运行时在后台运行,不会阻止程序的退出。当主线程退出时,守护线程也不会自动退出,而不需要等待它执行完毕。

方法一

在创建线程对象时,可以通过设置daemon属性为True来创建守护线程,例如:

import threading
import time

def worker():
    while True:
        print('Worker thread running')
        time.sleep(1)

# 创建守护线程
t = threading.Thread(target=worker, daemon=True)
# 启动线程
t.start()

# 主线程执行一些操作
print('Main thread running')
time.sleep(5)
print('Main thread finished')

输出结果为:

Worker thread runningMain thread running

Worker thread running
Worker thread running
Worker thread running
Worker thread running
Main thread finished

在这个示例中,我们创建了一个守护线程worker(),并将daemon属性设置为True。在主线程中,我们执行了一些操作,并休眠5秒钟。由于守护线程的存在,即使主线程已经结束,守护线程仍会在后台运行。

方法二

设置守护线程用Thread.setDaemon(bool)

#! -*-conding=: UTF-8 -*-
# 2023/5/6 16:06

import time
from threading import Thread

def task1():
    print("开始子线程1做任务1啦")
    time.sleep(1)  # 用time.sleep模拟任务耗时
    print("子线程1中的任务1结束啦")

def task2():
    print("开始子线程2做任务2啦")
    for i in range(5):
        print("任务2-{}".format(i))
        time.sleep(1)
    print("子线程2中的任务2结束啦")

if __name__ == '__main__':
    print("这里是主线程")
    # 创建线程对象
    t1 = Thread(target=task1)
    t2 = Thread(target=task2)
    t2.setDaemon(True)  # 设置为守护进程,必须在start之前
    # 启动
    t1.start()
    t2.start()
    time.sleep(1)
    print("主线程结束了")

输出结果为:

这里是主线程
开始子线程1做任务1啦
开始子线程2做任务2啦
任务2-0
主线程结束了
子线程1中的任务1结束啦任务2-1

守护线程的作用在于,当我们需要在程序运行时执行一些后台任务,但是不想让这些任务阻止程序的正常退出时,可以使用守护线程。
例如,在一个Web应用程序中,我们可能需要启动一个守护线程来定期清理缓存或者执行一些后台任务。

需要注意的是,守护线程无法完全控制其执行过程,因此不能用于一些必须在程序退出之前完成的任务。同时,守护线程不能访问一些主线程资源,例如共享内存或者打开的文件,因为这些资源可能会在主线程结束时被释放。

让主线程等待子线程结束 join

假如要让主线程等子线程结束,那么可以使用Thread.join()方法。

当调用线程对象的join()方法时,主线程将被阻塞,直到该线程执行完成或者超时。

以下是一个简单的示例:

import threading
import time

def worker():
    print('Worker thread started')
    time.sleep(2)
    print('Worker thread finished')

# 创建线程对象
t = threading.Thread(target=worker)
# 启动线程
t.start()

# 等待线程结束
t.join()

# 主线程继续执行
print('Main thread finished')

输出结果为:

Worker thread started
Worker thread finished
Main thread finished

在这个示例中,我们创建了一个子线程worker(),并使用start()方法启动线程。在主线程中,我们调用了线程对象的join()方法,让主线程等待子线程执行完毕。在子线程执行完毕后,主线程继续执行。

需要注意的是,join()方法还可以设置超时时间,以避免无限期等待线程的执行。例如:

import threading
import time

def worker():
    print('Worker thread started')
    time.sleep(2)
    print('Worker thread finished')

# 创建线程对象
t = threading.Thread(target=worker)
# 启动线程
t.start()

# 等待线程结束,最多等待3秒钟
t.join(3)

# 主线程继续执行
print('Main thread finished')

输出结果为:

Worker thread started
Worker thread finished
Main thread finished

在这个示例中,我们设置了join()方法的超时时间为3秒钟,即使子线程没有执行完成,主线程也会在3秒钟后继续执行。

线程共享资源可能引起什么问题?

在线程编程中,多个线程可能同时访问和修改同一个共享资源,例如全局变量、共享内存、文件等。如果没有进行适当的同步操作,就可能会引发以下问题:

竞态条件(Race Condition):当多个线程同时访问和修改同一个共享资源时,就可能会发生竞态条件。这种情况下,由于线程执行顺序的不确定性,可能会导致资源被错误地读取或写入,从而引发程序的错误或崩溃。

死锁(Deadlock):当多个线程都在等待另一个线程释放某个资源时,就可能会发生死锁。这种情况下,程序会永久地阻塞在这个状态下,无法继续执行。

活锁(Livelock):多个线程相互协作,但是由于某些原因无法前进,导致它们不断重试,最终导致系统陷入死循环。活锁是一种比死锁更难以诊断和解决的问题。

为了避免以上问题,我们可以使用线程同步机制来保护共享资源的访问。

例如,可以使用锁(Lock)信号量(Semaphore)条件变量(Condition)等机制来限制同时访问共享资源的线程数量,从而避免竞态条件。同时,也可以使用一些算法和策略来避免死锁和活锁等问题的发生。

下面是一些具体的例子,说明在多线程程序中共享资源可能引发的问题:

竞态条件
from threading import Thread

num = 0

def sum_o
  • 18
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Python线程是指在Python程序中同时执行多个线程的编程技术。使用多线程编程具有以下几个优点:首先,线程之间可以共享内存,这使得数据共享和通信更加容易。其次,创建线程的代价比创建进程小得多,因此使用多线程来实现多任务并发执行比使用多进程更高效。此外,Python语言内置了多线程功能支持,简化了多线程编程的复杂性\[1\]。 然而,需要注意的是,在Python 2.x版本中,存在全局解释器锁(GIL),它限制了同一进程中只能有一个线程执行Python字节码。这意味着在多核CPU上,Python的多线程并不能充分利用多核处理器的优势,因为只有拿到GIL锁的线程才能执行\[2\]。 尽管如此,多线程在IO密集型代码中仍然能够提升效率。在涉及文件处理、网络爬虫等需要进行IO操作的任务中,多线程可以避免不必要的IO等待时间,提高程序的执行效率。因此,对于IO密集型代码,Python的多线程是比较友好的\[3\]。 总结起来,Python的多线程编程具有一些优点,但也存在一些限制。在选择是否使用多线程时,需要根据具体的应用场景和需求来进行权衡和选择。 #### 引用[.reference_title] - *1* [python线详解(超详细)](https://blog.csdn.net/m0_67900727/article/details/123399522)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down28v1,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* *3* [python线详解](https://blog.csdn.net/weixin_39612023/article/details/109945251)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down28v1,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值