欢迎访问宙启技术站
智能推送

Python多线程函数:从头开始了解线程编程

发布时间:2023-06-08 03:28:14

Python的多线程编程是一种高效的编程模式,它可以大大提高程序的运行效率和并发能力。在Python多线程编程中,我们可以同时执行多个任务,提高程序并发性,同时保持程序的运行效率。

多线程的概念是,在同一时间内运行多个线程,实现同时执行多个任务的效果。在Python3中,我们可以使用threading模块来实现多线程编程。下面让我们从头开始了解Python多线程编程。

1. 创建线程函数

在Python中,我们可以通过定义一个线程类或者创建一个线程函数来创建线程。一个线程类通常需要继承自threading.Thread类,并且要实现run()方法,而创建线程函数则需要使用threading.Thread类的构造函数和target属性。下面分别来介绍这两种创建线程的方式。

1.1 使用线程类

下面是一个继承自threading.Thread类的线程类例子:

import threading

class MyThread(threading.Thread):

    def __init__(self, num):

        threading.Thread.__init__(self)

        self.num = num

    def run(self):

        print("Thread-{} start!".format(self.num))

t1 = MyThread(1)

t2 = MyThread(2)

t1.start()

t2.start()

t1.join()

t2.join()

我们首先定义了一个线程类MyThread,并且在初始化函数中保存了参数num。在run函数中,我们打印了线程启动的信息。

我们创建了两个线程对象t1和t2,分别将它们的num属性初始化为1和2。然后,我们调用它们的start()方法启动线程。最后,我们调用它们的join()方法等待线程运行结束。

1.2 使用线程函数

下面是一个创建线程函数的例子:

import threading

def my_thread_func(num):

    print("Thread-{} start!".format(num))

t1 = threading.Thread(target=my_thread_func, args=(1,))

t2 = threading.Thread(target=my_thread_func, args=(2,))

t1.start()

t2.start()

t1.join()

t2.join()

我们首先定义了一个线程函数my_thread_func,它接受一个num参数,并且在其中打印了线程启动的信息。

我们创建了两个线程对象t1和t2,将它们的target属性设置为my_thread_func函数,并且将其参数设置为(1,)和(2,)。然后,我们调用它们的start()方法启动线程。最后,我们调用它们的join()方法等待线程运行结束。

2. 线程同步

在多线程中,竞争资源是很常见的情况。为了避免线程之间的竞争冲突,我们要使用线程同步的方式来保证线程的有序执行。

2.1 Lock和RLock

在Python中,Lock和RLock是两种常用的线程同步方式。这两种方式都可以通过acquire()和release()方法来实现锁的获取和释放。

Lock是一种基本的锁机制,它只能以最基本的方式进行管理,它保证了只有一个线程可以访问共享资源。如果有多个线程同时在等待该锁,它们将被暂停,直到获得锁的线程释放它。

下面是一个Lock的例子:

import threading 

lock = threading.Lock()

count = 0

def my_func():

    global count

    lock.acquire()

    count += 1

    lock.release()

t1 = threading.Thread(target=my_func)

t2 = threading.Thread(target=my_func)

t1.start()

t2.start()

t1.join()

t2.join()

print("count: {}".format(count))

在这个例子中,我们定义了一个lock,在my_func函数中首先获取该锁,然后将count加1,最后释放该锁。

我们创建了两个线程对象t1和t2,它们执行的目标函数都为my_func。在run函数中,两个线程都会分别调用my_func函数。

最后,我们打印了count的值,可以看到最后的count值为2,说明这两个线程对count的访问已经被正确地同步。

RLock是一种可以重复调用同一个线程的锁机制。与Lock不同,如果同一线程调用RLock.acquire()多次,则不会被阻塞,因为每一次acquire操作都要对应有一次release操作才能释放该锁。

下面是一个RLock的例子:

import threading 

lock = threading.RLock()

count = 0

def my_func():

    global count

    with lock:

        count += 1

        with lock:

            count += 2

t1 = threading.Thread(target=my_func)

t2 = threading.Thread(target=my_func)

t1.start()

t2.start()

t1.join()

t2.join()

print("count: {}".format(count))

在这个例子中,我们同样定义了一个lock,在my_func函数中使用了with语句来获取该锁,在with语句中执行一些操作后,该锁会被自动释放。

我们创建了两个线程对象t1和t2,它们执行的目标函数都为my_func。在run函数中,两个线程都会分别调用my_func函数。

最后,我们打印了count的值,可以看到最后的count值为6,说明这两个线程对count的访问已经被正确地同步。

3. 共享资源管理

在多线程编程中,有时候需要共享一些资源,比如共享一个队列或者共享一段内存区域等。Python中提供了Queue和Array等工具来实现对共享资源的管理。

3.1 Queue队列

Queue(队列)是一种线程安全的数据结构,多个线程都可以从队列中获取元素。Python中的queue模块提供了三种队列类,分别是FIFOQueue、LifoQueue和PriorityQueue。

下面是一个FIFOQueue的例子:

import threading 

import queue

q = queue.Queue()

def my_func():

    while not q.empty():

        print(q.get())

for i in range(10):

    q.put(i)

t1 = threading.Thread(target=my_func)

t2 = threading.Thread(target=my_func)

t1.start()

t2.start()

t1.join()

t2.join()

在这个例子中,我们定义了一个FIFOQueue,并且用循环向队列中添加了10个元素。然后,我们定义了两个线程对象t1和t2,它们执行的目标函数都为my_func。在my_func函数中,我们循环从队列中取出元素并打印。

最后,我们可以看到,两个线程按照不同的顺序来获取队列中的元素。这说明Queue可以提供线程安全的共享资源管理方式。

3.2 Array共享内存

Array是一种共享内存的数据结构,它可以让多个线程共同访问一个共享内存区域。在Python中,我们可以使用multiprocessing模块中的Array来创建共享内存数组。

下面是一个共享内存数组的例子:

import multiprocessing

def my_func(arr):

    for i in range(len(arr)):

        arr[i] = 2 * arr[i]

a = multiprocessing.Array('i', 10)

for i in range(len(a)):

    a[i] = i

t1 = multiprocessing.Process(target=my_func, args=(a,))

t2 = multiprocessing.Process(target=my_func, args=(a,))

t1.start()

t2.start()

t1.join()

t2.join()

print(a[:])

在这个例子中,我们定义了一个Array,并且使用循环向其中添加了10个元素。然后,我们定义了两个进程对象t1和t2,它们执行的目标函数都为my_func。在my_func函数中,我们循环获取共享内存区域中的元素,