Python多线程函数:从头开始了解线程编程
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函数中,我们循环获取共享内存区域中的元素,
