python中关于多线程的操作可以使用thread和threading模块来实现,其中thread模块在Py3中已经改名为_thread,不再推荐使用。而threading模块是在thread之上进行了封装,也是推荐使用的多线程模块,本文主要基于threading模块进行介绍。在某些版本中thread模块可能不存在,要使用dump_threading来代替threading模块。
线程创建
threading模块中每个线程都是一个Thread对象,创建一个线程有两种方式,一种是将函数传递到Thread对象中执行,另一种是从Thread继承,然后重写run方法(是不是跟Java很像)。
下面使用这两种方法分别创建一个线程并同时执行
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
def
threadFunction
(
)
:
for
i
in
range
(
10
)
:
print
'ThreadFuction - %d'
%
i
time
.
sleep
(
random
.
randrange
(
0
,
2
)
)
class
ThreadClass
(
threading
.
Thread
)
:
def
__init__
(
self
)
:
threading
.
Thread
.
__init__
(
self
)
;
def
run
(
self
)
:
for
i
in
range
(
10
)
:
print
'ThreadClass - %d'
%
i
time
.
sleep
(
random
.
randrange
(
0
,
2
)
)
if
__name__
==
'__main__'
:
tFunc
=
threading
.
Thread
(
target
=
threadFunction
)
;
tCls
=
ThreadClass
(
)
tFunc
.
start
(
)
tCls
.
start
(
)
|
执行结果如下,可以看到两个线程在交替打印。至于空行和一行多个输出,是因为Py的print并不是线程安全的,在当前线程的print打印了部分内容后,准备打印换行之前,被别的线程中的print抢先,在换行之前打印了其它的内容。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
ThreadFuction
-
0
ThreadFuction
-
1
ThreadFuction
-
2
ThreadClass
-
0
ThreadFuction
-
3
ThreadClass
-
1
ThreadFuction
-
4
ThreadClass
-
2
ThreadClass
-
3
ThreadClass
-
4ThreadFuction
-
5
ThreadClass
-
5
ThreadClass
-
6
ThreadClass
-
7
ThreadClass
-
8
ThreadFuction
-
6ThreadClass
-
9
ThreadFuction
-
7
ThreadFuction
-
8
ThreadFuction
-
9
|
Thread类的构造函数定义如下
Thread类的成员变量和函数如下
这里的守护线程与Linux中的守护进程并不是一个概念。这里是指当所有守护线程退出后主程序才会退出,否则即使线程任务没有结束,只要不是守护线程,都会跟着主程序一起退出。而Linux中的守护进程定义正好相反,守护进程已经脱离父进程,不会随着父进程的结束而退出。
线程同步
线程同步是多线程中的一个核心问题,threading模块对线程同步有着良好的支持、包括线程特定数据、信号量、互斥锁、条件变量等。
线程特定数据
简而言之,线程特定数据就是线程独自持有的全局变量,相互之间的修改不会造成影响。
threading模块中使用local()方法生成一个线程独立对象,举例如下,其中sleep(1)是为了保证让子线程先运行完再运行接下来的语句。
1
2
3
4
5
6
7
8
9
10
11
|
data
=
threading
.
local
(
)
def
threadFunction
(
)
:
global
data
data
.
x
=
3
print
threading
.
currentThread
(
)
,
data
.
x
if
__name__
==
'__main__'
:
data
.
x
=
1
tFunc
=
threading
.
Thread
(
target
=
threadFunction
)
.
start
(
)
;
time
.
sleep
(
1
)
print
threading
.
current_thread
(
)
,
data
.
x
|
输出如下,可以看到,Thread-1中对data.x的修改并没有影响到主线程中data.x的值。
1
2
|
<
Thread
(
Thread
-
1
,
started
36208
)
>
3
<
_MainThread
(
MainThread
,
started
35888
)
>
1
|
互斥锁
threading中定义了两种锁:threading.Lock和threading.RLock。两者的不同在于后者是可重入锁,也就是说在一个线程内重复LOCK同一个锁不会发生死锁,这与POSIX中的PTHREAD_MUTEX_RECURSIVE也就是可递归锁的概念是相同的。
关于互斥锁的API很简单,只有三个函数————分配锁,上锁,解锁。
下面通过一个例子来说明互斥锁的使用。在之前的例子中,多线程print会造成混乱的输出,这里使用一个互斥锁,来保证每行一定只有一个输出。
1
2
3
4
5
6
7
8
9
|
def
threadFunction
(
arg
)
:
while
True
:
lock
.
acquire
(
)
print
'ThreadFuction - %d'
%
arg
lock
.
release
(
)
if
__name__
==
'__main__'
:
lock
=
threading
.
Lock
(
)
threading
.
Thread
(
target
=
threadFunction
,
args
=
(
1
,
)
)
.
start
(
)
;
threading
.
Thread
(
target
=
threadFunction
,
args
=
(
2
,
)
)
.
start
(
)
;
|
条件变量
条件变量总是与互斥锁一起使用的,threading中的条件变量默认绑定了一个RLock,也可以在初始化条件变量的时候传进去一个自己定义的锁。
可用的函数如下
下面这个例子使用条件变量来控制两个线程交替运行
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
num
=
0
def
threadFunction
(
arg
)
:
global
num
while
num
<
10
:
cond
.
acquire
(
)
while
num
%
2
!=
arg
:
cond
.
wait
(
)
print
'Thread %d - %d'
%
(
arg
,
num
)
num
+=
1
cond
.
notify
(
)
cond
.
release
(
)
if
__name__
==
'__main__'
:
cond
=
threading
.
Condition
(
)
threading
.
Thread
(
target
=
threadFunction
,
args
=
(
0
,
)
)
.
start
(
)
;
threading
.
Thread
(
target
=
threadFunction
,
args
=
(
1
,
)
)
.
start
(
)
;
|
输出如下
1
2
3
4
5
6
7
8
9
10
11
|
Thread
0
-
0
Thread
1
-
1
Thread
0
-
2
Thread
1
-
3
Thread
0
-
4
Thread
1
-
5
Thread
0
-
6
Thread
1
-
7
Thread
0
-
8
Thread
1
-
9
Thread
0
-
10
|
其实上面这个程序是有问题的,我们想打印的是0~9,但实际上10也被打印了出来,原因很简单,因为两个线程交替打印,使得num在一个线程中可能加2,从而导致10被打印出来,所以必须在打印前再次check。
原文:http://c4fun.cn/blog/2014/05/06/python-threading/
转载请注明:成长的对话 » python实现多线程教程