python:并发编程(七)

前言

本文将和大家一起探讨python的多线程并发编程(上篇),使用内置基本库threading来实现并发,先通过官方来简单使用这个模块。先打好基础,能够有个基本的用法与认知,后续文章,我们再进行详细使用。

本文为python并发编程的第七篇,上一篇文章地址如下:

python:并发编程(六)_Lion King的博客-CSDN博客

下一篇文章地址如下:

(暂无)
 

一、快速开始 

官方文档:threading --- 基于线程的并行 — Python 3.11.4 文档

1、线程本地数据

threading模块提供了线程本地数据(Thread-local data)的功能,可以让每个线程拥有自己独立的数据空间,不同线程之间的数据互不干扰。

可以使用threading.local()函数创建一个线程本地数据对象。每个线程通过访问这个对象来获取和设置自己独立的数据。每个线程对这个对象的操作都是针对自己的数据空间,不会影响其他线程。

下面是一个简单的示例:

import threading

# 创建线程本地数据对象
local_data = threading.local()

def set_data(value):
    # 设置线程本地数据
    local_data.value = value

def get_data():
    # 获取线程本地数据
    return local_data.value

def worker():
    # 在各个线程中设置和获取线程本地数据
    set_data(threading.current_thread().name)
    print(get_data())

# 创建多个线程并启动
threads = []
for i in range(5):
    t = threading.Thread(target=worker)
    threads.append(t)
    t.start()

# 等待所有线程执行完成
for t in threads:
    t.join()

每个线程在执行worker函数时,会将当前线程的名称作为数据设置到线程本地数据对象中,并在之后获取并打印出来。由于每个线程都有自己独立的数据空间,所以每个线程获取到的数据都是自己设置的,互不干扰。

2、线程对象

from threading import Thread

# 创建一个新的线程,并指定目标函数为print,参数为1
t = Thread(target=print, args=[1])

# 启动新线程,开始执行目标函数
t.start()

start()可以使用run()方法代替,然而,需要注意的是,调用run()方法并不会启动一个新线程来执行目标函数,而是在当前线程中直接调用目标函数。所以,要启动一个新线程并执行目标函数,应该使用start()方法,而不是run()方法。

在Python中,threading模块提供了Thread类来创建和操作线程对象。线程对象用于执行并发的代码块,使得多个代码块可以同时执行。

以下是一个简单的示例:

import threading

# 线程执行的目标函数
def worker():
    print("Thread executing")

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

# 启动线程
t.start()

# 等待线程执行完成
t.join()

print("Thread finished")

上述代码创建了一个线程对象t,指定了目标函数worker作为线程要执行的内容。然后通过调用start()方法来启动线程,线程开始执行目标函数中的代码块。在示例中,目标函数只是简单地打印一条消息。

使用join()方法可以让主线程等待子线程执行完成。这样确保主线程在子线程完成后再继续执行。最后,主线程打印一条消息来表示线程执行结束。

通过Thread类,你可以根据需要创建多个线程对象来执行并发的任务。每个线程对象都有自己的执行上下文,包括线程的状态、代码指针等。你可以使用线程对象的方法来控制线程的行为,如启动、暂停、终止等。

需要注意的是,线程对象不是直接调用目标函数,而是通过start()方法启动线程,由Python解释器在适当的时候调用目标函数。这样可以确保线程的正确启动和执行。

另外,线程对象还提供了其他一些方法和属性,如is_alive()用于检查线程是否处于活动状态,name属性用于获取线程的名称等。通过这些方法和属性,可以更加灵活地控制和管理线程的行为。

3、锁对象

threading模块中,锁对象用于控制多个线程之间对共享资源的访问。通过锁对象,可以确保在任意时刻只有一个线程可以访问共享资源,从而避免竞争条件和数据不一致的问题。

在Python中,可以使用Lock类来创建锁对象。下面是一个简单的示例:

import threading

# 创建锁对象
lock = threading.Lock()

# 共享资源
shared_resource = 0

# 线程函数
def worker():
    global shared_resource
    # 获取锁
    lock.acquire()
    try:
        # 访问共享资源
        shared_resource += 1
    finally:
        # 释放锁
        lock.release()

# 创建多个线程并启动
threads = []
for _ in range(5):
    t = threading.Thread(target=worker)
    t.start()
    threads.append(t)

# 等待所有线程执行完成
for t in threads:
    t.join()

print("Shared resource:", shared_resource)

在示例中,我们创建了一个锁对象lock,用于控制对shared_resource这个共享变量的访问。在每个线程的执行过程中,通过调用acquire()方法来获取锁,这样只有一个线程能够成功获取锁,其他线程会被阻塞。然后在try...finally块中访问共享资源,并在最后使用release()方法释放锁,以便其他线程可以继续访问。

通过使用锁对象,我们确保了对共享资源的安全访问,避免了多个线程同时修改共享资源导致的数据不一致问题。只有获取到锁的线程才能访问共享资源,其他线程在等待期间将被阻塞。这种机制确保了线程之间的同步和互斥。

需要注意的是,为了避免死锁情况的发生,我们应该遵循一定的锁使用规则,如避免在持有锁的情况下调用阻塞的操作,避免嵌套使用锁等。合理地使用锁对象可以确保线程间的数据一致性和并发安全性。

你可能感兴趣的:(python,python)