Python 中的线程-进程2

原文:https://www.cnblogs.com/i-honey/p/7823587.html

Python中实现多线程需要使用到 threading 库,其中每一个 Thread类 的实例控制一个线程。

Thread类

#类签名

1
2
def __init__( self , group = None , target = None , name = None ,
                  args = (), kwargs = None , * , daemon = None ):

  简单介绍一些初始化参数:

target: 指定线程由 run () 方法调用的可调用对象。默认为 None, 意味着不调用任何内容。

name: 指定该线程的名称。 在默认情况下,创建一个唯一的名称。

args: target调用的实参,元组格式。默认为 (),即不传参。

daemon: 为False表示父线程在运行结束时需要等待子线程结束才能结束程序,为True则表示父线程在运行结束时,子线程无论是否还有任务未完成都会跟随父进程退出,结束程序。

线程启动:

1
2
3
4
5
6
7
8
9
10
11
12
13
import threading
 
 
def worker(arg): #线程执行的目标函数
     print ( "I'm working {}" . format (arg))
     print ( "Fineshed" )
 
t = threading.Thread(target = worker,args = (threading.current_thread(),),name = "firstworker" ) #线程对象
t.start() #启动线程
 
运行结果:
I'm working <_MainThread(MainThread, started 10936 )>
Fineshed

  上面例子中,当函数执行完之后,线程也就跟着退出了。

线程的传参:

1
2
3
4
5
6
7
8
9
10
11
12
import threading
 
def add(x,y):
     print (x + y)
 
t = threading.Thread(target = add,args = ( 4 , 5 ))
t.start()
 
print ( "====end===" )
运行结果:
9
= = = = end = = =

  线程的传参和函数传参没有区别,只需要注意传入的必须为元祖格式。

线程退出:

如果线程中任务是无限循环语句,那这个线程将无法自动停止。

Python线程退出条件有以下几种:

1、线程内的函数语句执行完毕,线程自动结束

2、线程内的函数抛出未处理的异常

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import threading
import time
 
def worker(arg):
     while True :
         time.sleep( 1 )
         print ( "I'm working {}" . format (arg))
     print ( "Fineshed" )
 
t = threading.Thread(target = worker,args = (threading.current_thread(),),name = "firstworker" )
t.start()
运行结果:
I'm working <_MainThread(MainThread, stopped 2468 )>
I'm working <_MainThread(MainThread, stopped 2468 )>
I'm working <_MainThread(MainThread, stopped 2468 )>
...

  上面例子中,线程启动后,将一直循环下去,线程不会自动退出。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import threading
import time
 
def worker(arg):
     count = 0
     while True :
         if count > 5 :
             raise RuntimeError(count)
         time.sleep( 1 )
         print ( "I'm working {}" . format (arg))
         count + = 1
     print ( "Fineshed" )
 
t = threading.Thread(target = worker,args = (threading. enumerate (),))
t.start()
 
print ( "====end===" )
 
运行结果:
= = = = end = = =
I'm working [<_MainThread(MainThread, stopped 10992 )>]
I'm working [<_MainThread(MainThread, stopped 10992 )>]
I'm working [<_MainThread(MainThread, stopped 10992 )>]
I'm working [<_MainThread(MainThread, stopped 10992 )>]
I'm working [<_MainThread(MainThread, stopped 10992 )>]
I'm working [<_MainThread(MainThread, stopped 10992 )>]
Exception in thread Thread - 1 :
Traceback (most recent call last):
   File "C:/python/test.py" , line 8 , in worker
     raise RuntimeError(count)
RuntimeError: 6

  上面例子中,演示了触发异常自动退出线程。但最先打印的是主程序的"===end==="语句,是因为在程序中,主线程启动一个线程后,不会等待子线程执行完毕,就继续执行了后续语句,在执行完主线程语句后,发现还有子线程没有结束,于是等待子线程执行结束,子线程在运行时抛出了未处理的异常,最终子线程结束,主线程也随之结束。这里需要了解daemon线程和non-daemon线程,稍后就会介绍。

threading属性:

threading.current_thread()   返回当前线程对象
threading.main_thread() 返回主线程对象
threading.active_count() 返回处于Active状态的线程个数
threading.enumerate() 返回所有存活的线程的列表,不包括已经终止的线程和未启动的线程
threading.get_ident() 返回当前线程的ID,非0整数

举例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
import threading
import time
 
def showthreadinfo():
     print ( "current thread = {}" . format (threading.current_thread()))
     print ( "main thread  = {}" . format (threading.main_thread()))
     print ( "active thread count = {}" . format (threading.active_count()))
     print ( "active thread list = {}" . format (threading. enumerate ()))
     print ( "thread id = {}" . format (threading.get_ident()))
     print ( "~~~~~~~~~~~~~" )
 
def add(x,y):
     time.sleep( 1 )
     showthreadinfo() #子线程中调用
     print (x + y)
 
showthreadinfo() #主线程中调用
time.sleep( 1 )
 
t = threading.Thread(target = add,args = ( 4 , 5 ))
t.start()
 
print ( "====end===" )
 
运行结果:
current thread = <_MainThread(MainThread, started 192 )>
main thread  = <_MainThread(MainThread, started 192 )>
active thread count = 1
active thread list = [<_MainThread(MainThread, started 192 )>]
thread id = 192
~~~~~~~~~~~~~
= = = = end = = =
current thread = <Thread(Thread - 1 , started 8424 )>
main thread  = <_MainThread(MainThread, stopped 192 )>
active thread count = 2
active thread list = [<_MainThread(MainThread, stopped 192 )>, <Thread(Thread - 1 , started 8424 )>]
thread id = 8424
~~~~~~~~~~~~~
9

  上面例子中,在主线程中只能看到存活的只有自己,因为子线程还没有启动,且它的父线程就是它自己。子线程启动时,它的名字为Thread-1,这个名字是解释器自动命名的,如果定义线程对象时添加了name="threadName",则这里显示的就是threadName;同时,子线程的父线程就是主线程,也就是说谁启动的线程谁就是它的父线程;子线程能看到的存活线程有父线程和自身。

Thread实例的属性:

threading.current_thread().name        线程名,只是一个标识符,可以使用getName()、setName()获取和运行时重命名。
threading.current_thread().ident 线程ID,非0整数。线程启动后才会有ID,否则为None。线程退出,此ID依旧可以访问。此ID可以重复使用
threading.current_thread().is_alive() 返回线程是否存活,布尔值,True或False。

举例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
import threading
import time
 
def worker():
     count = 1
     while True :
         if count > = 6 :
             break
         time.sleep( 1 )
         count + = 1
         print ( "thread name = {}" . format (threading.current_thread().name))
 
t = threading.Thread(target = worker,name = "MyThread" )
t.start()
 
while True :
     time.sleep( 1.1 )
     if t.is_alive():
         print ( "{} {} alive" . format (t.name,t.ident))
     else :
         print ( "{} {} alive" . format (t.name, t.ident))
         t.start()
 
print ( "====end===" )
 
运行结果:
thread name = MyThread
MyThread 9400 alive
thread name = MyThread
MyThread 9400 alive
thread name = MyThread
MyThread 9400 alive
thread name = MyThread
MyThread 9400 alive
thread name = MyThread
MyThread 9400 alive
Traceback (most recent call last):
   File "C:/python/test.py" , line 22 , in <module>
     t.start()
     raise RuntimeError( "threads can only be started once" )
RuntimeError: threads can only be started once

  从上面例子中可以看到子线程存活时的名字和线程ID,但在线程退出后,尝试再次启动线程时,抛出RuntimeError异常,表明线程对象在定义后只能启动一次。

 举例 getName()和setName():

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
import threading
import time
 
def add(x,y):
     for _ in range ( 5 ):
         time.sleep( 1 )
         print ( "x+y={}" . format (x + y))
 
t = threading.Thread(target = add,name = "MyThread" ,args = ( 6 , 7 ))
t.start()
 
while True :
     time.sleep( 1 )
     if t.is_alive():
         print ( "{} {} alive" . format (t.name,t.ident))
         print ( "Thread name" ,t.getName())
         t.setName( "MyThreadTwo" )
     else :
         print ( "{} {} alive" . format (t.name, t.ident))
         print ( "Thread abort...." )
         break
         # t.start()
 
print ( "====end===" )
 
运行结果:
MyThread 2564 alive
Thread name MyThread
x + y = 13
MyThreadTwo 2564 alive
Thread name MyThreadTwo
x + y = 13
MyThreadTwo 2564 alive
Thread name MyThreadTwo
x + y = 13
MyThreadTwo 2564 alive
Thread name MyThreadTwo
x + y = 13
MyThreadTwo 2564 alive
Thread name MyThreadTwo
x + y = 13
MyThreadTwo 2564 alive
Thread abort....
= = = = end = = =

  上面例子演示了在运行时获取线程名和重命名线程名。

线程的start()和run()方法:

start():

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
import threading
import time
 
def add(x,y):
     for _ in range ( 5 ):
         time.sleep( 0.5 )
         print ( "x+y={}" . format (x + y))
 
class MyThread(threading.Thread):
     def start( self ):
         print ( 'start~~~~~~~~~~' )
         super ().start()
 
     def run( self ):
         print ( 'run~~~~~~~~~~~~' )
         super ().run()  #调用父类的start()和run()方法
 
 
t = MyThread(target = add,name = "MyThread" ,args = ( 6 , 7 ))
t.start()
# t.run()
print ( "====end===" )
 
运行结果:
start~~~~~~~~~~
run~~~~~~~~~~~~
= = = = end = = =
x + y = 13
x + y = 13
x + y = 13
x + y = 13
x + y = 13

  从上面的例子中,可以看出start()方法会先运行start()方法,再运行run()方法。

跟进一下start() 方法源码中的调用过程:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
1 def start( self ):
     _start_new_thread( self ._bootstrap, ())
     ....
 
2 、_start_new_thread = _thread.start_new_thread
 
3 def start_new_thread(function, args, kwargs = None ):
     pass
 
4 def _bootstrap( self ):
     self ._bootstrap_inner()
 
5 def _bootstrap_inner( self ):
     ....
     try :
         self .run() #最终start()方法调用了run()方法
     except SystemExit:
         pass

  从上面跟踪源码的过程大概了解了start()方法如何调用到了run()方法。

run()方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import threading
import time
 
def add(x,y):
     for _ in range ( 5 ):
         time.sleep( 0.5 )
         print ( "x+y={}" . format (x + y))
 
class MyThread(threading.Thread):
     def start( self ):
         print ( 'start~~~~~~~~~~' )
         super ().start()
 
     def run( self ):
         print ( 'run~~~~~~~~~~~~' )
         super ().run()  #调用父类的start()和run()方法
 
 
t = MyThread(target = add,name = "MyThread" ,args = ( 6 , 7 ))
# t.start()
t.run()
print ( "====end===" )
 
运行结果:
run~~~~~~~~~~~~
x + y = 13
x + y = 13
x + y = 13
x + y = 13
x + y = 13
= = = = end = = =

  上面例子中,运行线程的run()方法只能调用到run()方法。

跟踪一下run() 方法在源码中的调用过程:

1
2
3
4
5
6
7
8
9
10
11
1 def __init__( self , group = None , target = None , name = None ,
                  args = (), kwargs = None , * , daemon = None ):
     self ._target = target
     self ._args = args
     self ._kwargs = kwargs
     ....
 
2 def run( self ):
     if self ._target:
         self ._target( * self ._args, * * self ._kwargs)
     ....

  可以看出,_target是我们传入的目标函数,run()方法其实就类似一个装饰器,最终还是将_args 和_kwargs 参数传入目标函数运行,返回结果。

start() --> run() --> _target()

run() --> _target()

上面两个例子简单介绍了start()方法和run()方法的调用,下一篇文章再详细看一下它们到底有什么区别。

总结:

本文主要介绍了: Thread类、线程启动、线程的传参、线程退出、threading属性、Thread实例的属性、举例getName()和setName()、线程的start()和run()方法

猜你喜欢

转载自www.cnblogs.com/kaishirenshi/p/9718004.html