Python入门之——线程threading(Thread类)简介

1. 简介

在Python中,线程是轻量级的执行单元,每个线程都有自己的执行序列,且并发执行。线程模块threading是Python中使用最广泛的模块之一,它提供了一种创建并发线程的方法,且与操作系统无关。

2. 线程的创建

2.1 threading.Thread()

使用threading.Thread()方法可以创建一个线程对象,如下所示:

import threading

#定义一个函数作为线程的执行体

def thread_main(name):

print("Thread %s is running" % name)

def main():

#创建线程1,指定函数thread_main为线程的执行体,传递参数

t1 = threading.Thread(target=thread_main, args=("Thread_1",))

#创建线程2,指定函数thread_main为线程的执行体,传递参数

t2 = threading.Thread(target=thread_main, args=("Thread_2",))

#让线程1开始执行

t1.start()

#让线程2开始执行

t2.start()

if __name__ == '__main__':

main()

在上面的代码中,我们使用threading.Thread()方法创建了两个线程对象t1和t2,指定了它们的执行函数和参数,然后分别启动这两个线程。运行代码输出结果如下:

Thread Thread_1 is running

Thread Thread_2 is running

由此可见,两个线程是并发执行的。

2.2 继承Thread类

除了可以使用threading.Thread()方法创建线程对象外,还可以通过继承Thread类来实现创建线程的方法,如下所示:

import threading

#创建一个MyThread类,继承Thread类

class MyThread(threading.Thread):

def __init__(self, name):

threading.Thread.__init__(self)

self.name = name

def run(self):

print("Thread %s is running" % self.name)

def main():

t1 = MyThread("Thread_1")

t2 = MyThread("Thread_2")

t1.start()

t2.start()

if __name__ == '__main__':

main()

在上面的代码中,我们创建了一个MyThread类,继承了Thread类,并重载了其中的run()方法,用于指定线程的执行体。接着,我们创建了两个MyThread对象t1和t2,并启动这两个线程。运行代码输出结果如下:

Thread Thread_1 is running

Thread Thread_2 is running

与2.1中的代码输出结果相同,表明两个线程也是并发执行的。

3. 线程的运行状态

线程的生命周期可分为5个状态:

新建状态(New):线程被创建但还没有开始执行。

就绪状态(Ready):等待CPU调度,一旦得到CPU资源就立即开始执行。

运行状态(Running):线程正在执行。

阻塞状态(Blocked):执行过程中被暂停,等待某个条件的出现,如I/O操作。

终止状态(Terminated):线程执行结束或发生异常而终止。

4. 线程的属性和方法

4.1 属性

4.1.1 name属性

Thread对象的name属性用于获取或设置线程名称,默认名称为Thread-i,i为线程的编号。

import threading

def thread_main(name):

print("Thread %s is running" % name)

def main():

t1 = threading.Thread(target=thread_main, args=("Thread_1",), name="MyThread_1")

t2 = threading.Thread(target=thread_main, args=("Thread_2",), name="MyThread_2")

print(t1.name) #输出结果:MyThread_1

print(t2.name) #输出结果:MyThread_2

t1.start()

t2.start()

if __name__ == '__main__':

main()

在上面的代码中,我们创建了两个线程对象t1和t2,分别指定了它们的name属性。运行代码输出结果如下:

MyThread_1

MyThread_2

Thread Thread_1 is running

Thread Thread_2 is running

由此可见,线程的name属性可用于获取或设置线程的名称。

4.1.2 ident属性

Thread对象的ident属性用于获取线程的标识符,如果线程还没有开始执行,则id值为None。

import threading

def thread_main(name):

print("Thread %s id is %s" % (name, threading.currentThread().ident))

def main():

t1 = threading.Thread(target=thread_main, args=("Thread_1",))

t2 = threading.Thread(target=thread_main, args=("Thread_2",))

print(t1.ident) #输出结果:None

print(t2.ident) #输出结果:None

t1.start()

t2.start()

if __name__ == '__main__':

main()

在上面的代码中,我们创建了两个线程对象t1和t2,分别获取了它们的ident属性。运行代码输出结果如下:

None

None

Thread Thread_1 id is 123145103792384

Thread Thread_2 id is 123145111185088

由此可见,在线程还没有开始执行时,ident属性的值为None,当线程开始执行时,ident属性的值才会被赋值。

4.2 方法

4.2.1 start()方法

Thread对象的start()方法用于启动线程,使其进入就绪状态,等待CPU调度执行。

start()方法只能被调用一次,如果多次调用会抛出RuntimeError异常。

4.2.2 run()方法

Thread对象的run()方法用于指定线程的执行体。默认情况下,run()方法会调用target参数指定的函数。

当使用继承Thread类来创建线程时,必须重载run()方法,并在其中指定线程的执行体。

4.2.3 join([timeout])方法

Thread对象的join([timeout])方法用于使主线程等待子线程结束。

timeout参数指定等待的最长时间(秒),如果在指定时间内线程还没有结束,则不再等待,返回None。

import threading

import time

def thread_main(name):

print("Thread %s is running" % name)

time.sleep(1) #模拟线程执行耗时

print("Thread %s is done" % name)

def main():

t1 = threading.Thread(target=thread_main, args=("Thread_1",))

t2 = threading.Thread(target=thread_main, args=("Thread_2",))

t1.start()

t2.start()

t1.join()

t2.join()

if __name__ == '__main__':

main()

在上面的代码中,我们创建了两个线程对象t1和t2,在主线程中依次启动它们。接着,调用t1.join()和t2.join()方法,使主线程等待t1和t2线程结束。运行代码输出结果如下:

Thread Thread_1 is running

Thread Thread_2 is running

Thread Thread_1 is done

Thread Thread_2 is done

由此可见,join()方法能够保证在子线程结束前不会退出主线程。

4.2.4 isAlive()方法

Thread对象的isAlive()方法用于判断线程是否还存在。

如果线程还存在,则返回True,否则返回False。

import threading

import time

def thread_main(name):

print("Thread %s is running" % name)

time.sleep(1) #模拟线程执行耗时

print("Thread %s is done" % name)

def main():

t1 = threading.Thread(target=thread_main, args=("Thread_1",))

t2 = threading.Thread(target=thread_main, args=("Thread_2",))

t1.start()

t2.start()

time.sleep(0.5) #等待线程执行一段时间

print(t1.isAlive()) #输出结果:True

print(t2.isAlive()) #输出结果:True

t1.join()

t2.join()

print(t1.isAlive()) #输出结果:False

print(t2.isAlive()) #输出结果:False

if __name__ == '__main__':

main()

在上面的代码中,我们创建了两个线程对象t1和t2,并启动它们。然后等待线程执行一段时间后,依次输出它们的isAlive()方法的返回值。接着,等待两个线程执行结束后,再次输出它们的isAlive()方法的返回值。运行代码输出结果如下:

Thread Thread_1 is running

Thread Thread_2 is running

True

True

Thread Thread_1 is done

Thread Thread_2 is done

False

False

由此可见,通过isAlive()方法可以判断线程是否还存在。

5. 线程同步

对于多个线程对同一资源进行读写操作时,会出现竞争状态,可能导致数据不一致或数据丢失等问题。为了解决这种问题,需要使用线程同步技术。

5.1 Lock对象

Lock对象用于控制多个线程对共享资源的访问,当一个线程获得了Lock对象的锁(调用acquire()方法),其他线程就不能再访问共享资源,只有当该线程释放了锁(调用release()方法)后,其他线程才可以再次获得锁。

import threading

import time

shared_resource = 0 #共享资源

lock = threading.Lock() #Lock对象

#函数作为线程的执行体

def thread_main():

global shared_resource

lock.acquire() #获得锁

for i in range(1000000):

shared_resource += 1

lock.release() #释放锁

def main():

threads = []

for i in range(4):

threads.append(threading.Thread(target=thread_main))

threads[i].start()

for i in range(4):

threads[i].join()

print(shared_resource)

if __name__ == '__main__':

main()

在上面的代码中,我们定义了一个全局变量shared_resource,并创建了一个Lock对象lock作为全局变量。然后,我们定义了一个thread_main()函数作为线程的执行体,该函数在获得锁并修改全局变量shared_resource后,释放锁。

在主线程中,我们创建了4个线程,并启动它们。然后等待它们执行结束,并输出最终的共享资源shared_resource。

运行代码输出结果如下:

4000000

由此可见,通过Lock对象可以控制多个线程对共享资源的访问,避免了竞态条件。

6. 总结

本文介绍了Python中线程的相关知识,包括线程的创建、线程的运行状态、线程的属性和方法、线程同步等内容。通过学习本文,读者可以初步了解Python中线程的基本用法,并能通过使用Lock对象等线程同步技术,避免多个线程并发执行时出现的竞态条件。

免责声明:本文来自互联网,本站所有信息(包括但不限于文字、视频、音频、数据及图表),不保证该信息的准确性、真实性、完整性、有效性、及时性、原创性等,版权归属于原作者,如无意侵犯媒体或个人知识产权,请来电或致函告之,本站将在第一时间处理。猿码集站发布此文目的在于促进信息交流,此文观点与本站立场无关,不承担任何责任。

后端开发标签