博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Python 中的线程-进程2
阅读量:7217 次
发布时间:2019-06-29

本文共 9126 字,大约阅读时间需要 30 分钟。

原文: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()方法

转载于:https://www.cnblogs.com/kaishirenshi/p/9718004.html

你可能感兴趣的文章
MySQL 用户连接与用户线程
查看>>
RabbitMq、ActiveMq、Kafka和Redis做Mq对比
查看>>
C# 图片处理(压缩、剪裁,转换,优化)
查看>>
Linux bridge-utils tunctl 使用
查看>>
Leetcode Pascal&#39;s Triangle II
查看>>
运行shell脚本报错 &#39;\357\273\277&#39;: command not found 解决的方法
查看>>
android studio 0.8.1使用和遇到问题解决
查看>>
云服务器ECS选购集锦之六区域选择帮助
查看>>
云虚机选购指南之二云虚拟主机试用帮助文档
查看>>
女友眼中的IT男
查看>>
Excel连接
查看>>
java基础-多线程学习
查看>>
WPF打印原理,自定义打印
查看>>
HTML5 5
查看>>
箭头css
查看>>
Python入门,以及简单爬取网页文本内容
查看>>
顺丰科技笔试回忆
查看>>
excel技巧
查看>>
通用防SQL注入漏洞程序(Global.asax方式)
查看>>
服务器进程为何通常fork()两次
查看>>