- ubuntu12.04环境下使用kvm ioctl接口实现最简单的虚拟机
- Ubuntu 通过无线网络安装Ubuntu Server启动系统后连接无线网络的方法
- 在Ubuntu上搭建网桥的方法
- ubuntu 虚拟机上网方式及相关配置详解
CFSDN坚持开源创造价值,我们致力于搭建一个资源共享平台,让每一个IT人在这里找到属于你的精彩世界.
这篇CFSDN的博客文章Python3中最常用的5种线程锁实例总结由作者收集整理,如果你对这篇文章有兴趣,记得点赞哟.
本章节将继续围绕threading模块讲解,基本上是纯理论偏多.
对于日常开发者来讲很少会使用到本章节的内容,但是对框架作者等是必备知识,同时也是高频的面试常见问题.
官方文档 。
线程安全是多线程或多进程编程中的一个概念,在拥有共享数据的多条线程并行执行的程序中,线程安全的代码会通过同步机制保证各个线程都可以正常且正确的执行,不会出现数据污染等意外情况.
线程安全的问题最主要还是由线程切换导致的,比如一个房间(进程)中有10颗糖(资源),除此之外还有3个小人(1个主线程、2个子线程),当小人A吃了3颗糖后被系统强制进行休息时他认为还剩下7颗糖,而当小人B工作后又吃掉了3颗糖,那么当小人A重新上岗时会认为糖还剩下7颗,但是实际上只有4颗了.
上述例子中线程A和线程B的数据不同步,这就是线程安全问题,它可能导致非常严重的意外情况发生,我们按下面这个示例来进行说明.
下面有一个数值num初始值为0,我们开启2条线程:
结果可能会令人咋舌,num最后并不是我们所想象的结果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
|
import
threading
num
=
0
def
add():
global
num
for
i
in
range
(
10_000_000
):
num
+
=
1
def
sub():
global
num
for
i
in
range
(
10_000_000
):
num
-
=
1
if
__name__
=
=
"__main__"
:
subThread01
=
threading.Thread(target
=
add)
subThread02
=
threading.Thread(target
=
sub)
subThread01.start()
subThread02.start()
subThread01.join()
subThread02.join()
print
(
"num result : %s"
%
num)
# 结果三次采集
# num result : 669214
# num result : -1849179
# num result : -525674
|
上面这就是一个非常好的案例,想要解决这个问题就必须通过锁来保障线程切换的时机.
需要我们值得留意的是,在Python基本数据类型中list、tuple、dict本身就是属于线程安全的,所以如果有多个线程对这3种容器做操作时,我们不必考虑线程安全问题.
锁是Python提供给我们能够自行操控线程切换的一种手段,使用锁可以让线程的切换变的有序.
一旦线程的切换变的有序后,各个线程之间对数据的访问、修改就变的可控,所以若要保证线程安全,就必须使用锁.
threading模块中提供了5种最常见的锁,下面是按照功能进行划分:
Lock锁的称呼有很多,如:
它们是什么意思呢?如下所示:
下面是threading模块与同步锁提供的相关方法:
方法 | 描述 |
---|---|
threading.Lock() | 返回一个同步锁对象 |
lockObject.acquire(blocking=True, timeout=1) | 上锁,当一个线程在执行被上锁代码块时,将不允许切换到其他线程运行,默认锁失效时间为1秒 |
lockObject.release() | 解锁,当一个线程在执行未被上锁代码块时,将允许系统根据策略自行切换到其他线程中运行 |
lockObject.locaked() | 判断该锁对象是否处于上锁状态,返回一个布尔值 |
同步锁一次只能放行一个线程,一个被加锁的线程在运行时不会将执行权交出去,只有当该线程被解锁时才会将执行权通过系统调度交由其他线程.
如下所示,使用同步锁解决最上面的问题:
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
|
import
threading
num
=
0
def
add():
lock.acquire()
global
num
for
i
in
range
(
10_000_000
):
num
+
=
1
lock.release()
def
sub():
lock.acquire()
global
num
for
i
in
range
(
10_000_000
):
num
-
=
1
lock.release()
if
__name__
=
=
"__main__"
:
lock
=
threading.Lock()
subThread01
=
threading.Thread(target
=
add)
subThread02
=
threading.Thread(target
=
sub)
subThread01.start()
subThread02.start()
subThread01.join()
subThread02.join()
print
(
"num result : %s"
%
num)
# 结果三次采集
# num result : 0
# num result : 0
# num result : 0
|
这样这个代码就完全变成了串行的状态,对于这种计算密集型I/O业务来说,还不如直接使用串行化单线程执行来得快,所以这个例子仅作为一个示例,不能概述锁真正的用途.
对于同步锁来说,一次acquire()必须对应一次release(),不能出现连续重复使用多次acquire()后再重复使用多次release()的操作,这样会引起死锁造成程序的阻塞,完全不动了,如下所示:
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
|
import
threading
num
=
0
def
add():
lock.acquire()
# 上锁
lock.acquire()
# 死锁
# 不执行
global
num
for
i
in
range
(
10_000_000
):
num
+
=
1
lock.release()
lock.release()
def
sub():
lock.acquire()
# 上锁
lock.acquire()
# 死锁
# 不执行
global
num
for
i
in
range
(
10_000_000
):
num
-
=
1
lock.release()
lock.release()
if
__name__
=
=
"__main__"
:
lock
=
threading.Lock()
subThread01
=
threading.Thread(target
=
add)
subThread02
=
threading.Thread(target
=
sub)
subThread01.start()
subThread02.start()
subThread01.join()
subThread02.join()
print
(
"num result : %s"
%
num)
|
由于threading.Lock()对象中实现了__enter__()与__exit__()方法,故我们可以使用with语句进行上下文管理形式的加锁解锁操作:
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
num
=
0
def
add():
with lock:
# 自动加锁
global
num
for
i
in
range
(
10_000_000
):
num
+
=
1
# 自动解锁
def
sub():
with lock:
# 自动加锁
global
num
for
i
in
range
(
10_000_000
):
num
-
=
1
# 自动解锁
if
__name__
=
=
"__main__"
:
lock
=
threading.Lock()
subThread01
=
threading.Thread(target
=
add)
subThread02
=
threading.Thread(target
=
sub)
subThread01.start()
subThread02.start()
subThread01.join()
subThread02.join()
print
(
"num result : %s"
%
num)
# 结果三次采集
# num result : 0
# num result : 0
# num result : 0
|
递归锁是同步锁的一个升级版本,在同步锁的基础上可以做到连续重复使用多次acquire()后再重复使用多次release()的操作,但是一定要注意加锁次数和解锁次数必须一致,否则也将引发死锁现象.
下面是threading模块与递归锁提供的相关方法:
方法 | 描述 |
---|---|
threading.RLock() | 返回一个递归锁对象 |
lockObject.acquire(blocking=True, timeout=1) | 上锁,当一个线程在执行被上锁代码块时,将不允许切换到其他线程运行,默认锁失效时间为1秒 |
lockObject.release() | 解锁,当一个线程在执行未被上锁代码块时,将允许系统根据策略自行切换到其他线程中运行 |
lockObject.locaked() | 判断该锁对象是否处于上锁状态,返回一个布尔值 |
以下是递归锁的简单使用,下面这段操作如果使用同步锁则会发生死锁现象,但是递归锁不会:
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
|
import
threading
num
=
0
def
add():
lock.acquire()
lock.acquire()
global
num
for
i
in
range
(
10_000_000
):
num
+
=
1
lock.release()
lock.release()
def
sub():
lock.acquire()
lock.acquire()
global
num
for
i
in
range
(
10_000_000
):
num
-
=
1
lock.release()
lock.release()
if
__name__
=
=
"__main__"
:
lock
=
threading.RLock()
subThread01
=
threading.Thread(target
=
add)
subThread02
=
threading.Thread(target
=
sub)
subThread01.start()
subThread02.start()
subThread01.join()
subThread02.join()
print
(
"num result : %s"
%
num)
# 结果三次采集
# num result : 0
# num result : 0
# num result : 0
|
由于threading.RLock()对象中实现了__enter__()与__exit__()方法,故我们可以使用with语句进行上下文管理形式的加锁解锁操作:
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
num
=
0
def
add():
with lock:
# 自动加锁
global
num
for
i
in
range
(
10_000_000
):
num
+
=
1
# 自动解锁
def
sub():
with lock:
# 自动加锁
global
num
for
i
in
range
(
10_000_000
):
num
-
=
1
# 自动解锁
if
__name__
=
=
"__main__"
:
lock
=
threading.RLock()
subThread01
=
threading.Thread(target
=
add)
subThread02
=
threading.Thread(target
=
sub)
subThread01.start()
subThread02.start()
subThread01.join()
subThread02.join()
print
(
"num result : %s"
%
num)
# 结果三次采集
# num result : 0
# num result : 0
# num result : 0
|
条件锁是在递归锁的基础上增加了能够暂停线程运行的功能。并且我们可以使用wait()与notify()来控制线程执行的个数.
注意:条件锁可以自由设定一次放行几个线程.
下面是threading模块与条件锁提供的相关方法:
方法 | 描述 |
---|---|
threading.Condition() | 返回一个条件锁对象 |
lockObject.acquire(blocking=True, timeout=1) | 上锁,当一个线程在执行被上锁代码块时,将不允许切换到其他线程运行,默认锁失效时间为1秒 |
lockObject.release() | 解锁,当一个线程在执行未被上锁代码块时,将允许系统根据策略自行切换到其他线程中运行 |
lockObject.wait(timeout=None) | 将当前线程设置为“等待”状态,只有该线程接到“通知”或者超时时间到期之后才会继续运行,在“等待”状态下的线程将允许系统根据策略自行切换到其他线程中运行 |
lockObject.wait_for(predicate, timeout=None) | 将当前线程设置为“等待”状态,只有该线程的predicate返回一个True或者超时时间到期之后才会继续运行,在“等待”状态下的线程将允许系统根据策略自行切换到其他线程中运行。注意:predicate参数应当传入一个可调用对象,且返回结果为bool类型 |
lockObject.notify(n=1) | 通知一个当前状态为“等待”的线程继续运行,也可以通过参数n通知多个 |
lockObject.notify_all() | 通知所有当前状态为“等待”的线程继续运行 |
下面这个案例会启动10个子线程,并且会立即将10个子线程设置为等待状态.
然后我们可以发送一个或者多个通知,来恢复被等待的子线程继续运行:
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
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
|
import
threading
currentRunThreadNumber
=
0
maxSubThreadNumber
=
10
def
task():
global
currentRunThreadNumber
thName
=
threading.currentThread().name
condLock.acquire()
# 上锁
print
(
"start and wait run thread : %s"
%
thName)
condLock.wait()
# 暂停线程运行、等待唤醒
currentRunThreadNumber
+
=
1
print
(
"carry on run thread : %s"
%
thName)
condLock.release()
# 解锁
if
__name__
=
=
"__main__"
:
condLock
=
threading.Condition()
for
i
in
range
(maxSubThreadNumber):
subThreadIns
=
threading.Thread(target
=
task)
subThreadIns.start()
while
currentRunThreadNumber < maxSubThreadNumber:
notifyNumber
=
int
(
input
(
"Please enter the number of threads that need to be notified to run:"
))
condLock.acquire()
condLock.notify(notifyNumber)
# 放行
condLock.release()
print
(
"main thread run end"
)
# 先启动10个子线程,然后这些子线程会全部变为等待状态
# start and wait run thread : Thread-1
# start and wait run thread : Thread-2
# start and wait run thread : Thread-3
# start and wait run thread : Thread-4
# start and wait run thread : Thread-5
# start and wait run thread : Thread-6
# start and wait run thread : Thread-7
# start and wait run thread : Thread-8
# start and wait run thread : Thread-9
# start and wait run thread : Thread-10
# 批量发送通知,放行特定数量的子线程继续运行
# Please enter the number of threads that need to be notified to run:5 # 放行5个
# carry on run thread : Thread-4
# carry on run thread : Thread-3
# carry on run thread : Thread-1
# carry on run thread : Thread-2
# carry on run thread : Thread-5
# Please enter the number of threads that need to be notified to run:5 # 放行5个
# carry on run thread : Thread-8
# carry on run thread : Thread-10
# carry on run thread : Thread-6
# carry on run thread : Thread-9
# carry on run thread : Thread-7
# Please enter the number of threads that need to be notified to run:1
# main thread run end
|
由于threading.Condition()对象中实现了__enter__()与__exit__()方法,故我们可以使用with语句进行上下文管理形式的加锁解锁操作:
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
currentRunThreadNumber
=
0
maxSubThreadNumber
=
10
def
task():
global
currentRunThreadNumber
thName
=
threading.currentThread().name
with condLock:
print
(
"start and wait run thread : %s"
%
thName)
condLock.wait()
# 暂停线程运行、等待唤醒
currentRunThreadNumber
+
=
1
print
(
"carry on run thread : %s"
%
thName)
if
__name__
=
=
"__main__"
:
condLock
=
threading.Condition()
for
i
in
range
(maxSubThreadNumber):
subThreadIns
=
threading.Thread(target
=
task)
subThreadIns.start()
while
currentRunThreadNumber < maxSubThreadNumber:
notifyNumber
=
int
(
input
(
"Please enter the number of threads that need to be notified to run:"
))
with condLock:
condLock.notify(notifyNumber)
# 放行
print
(
"main thread run end"
)
|
事件锁是基于条件锁来做的,它与条件锁的区别在于一次只能放行全部,不能放行任意个数量的子线程继续运行.
我们可以将事件锁看为红绿灯,当红灯时所有子线程都暂停运行,并进入“等待”状态,当绿灯时所有子线程都恢复“运行”.
下面是threading模块与事件锁提供的相关方法:
方法 | 描述 |
---|---|
threading.Event() | 返回一个事件锁对象 |
lockObject.clear() | 将事件锁设为红灯状态,即所有线程暂停运行 |
lockObject.is_set() | 用来判断当前事件锁状态,红灯为False,绿灯为True |
lockObject.set() | 将事件锁设为绿灯状态,即所有线程恢复运行 |
lockObject.wait(timeout=None) | 将当前线程设置为“等待”状态,只有该线程接到“绿灯通知”或者超时时间到期之后才会继续运行,在“等待”状态下的线程将允许系统根据策略自行切换到其他线程中运行 |
事件锁不能利用with语句来进行使用,只能按照常规方式.
如下所示,我们来模拟线程和红绿灯的操作,红灯停,绿灯行:
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
45
46
|
import
threading
maxSubThreadNumber
=
3
def
task():
thName
=
threading.currentThread().name
print
(
"start and wait run thread : %s"
%
thName)
eventLock.wait()
# 暂停运行,等待绿灯
print
(
"green light, %s carry on run"
%
thName)
print
(
"red light, %s stop run"
%
thName)
eventLock.wait()
# 暂停运行,等待绿灯
print
(
"green light, %s carry on run"
%
thName)
print
(
"sub thread %s run end"
%
thName)
if
__name__
=
=
"__main__"
:
eventLock
=
threading.Event()
for
i
in
range
(maxSubThreadNumber):
subThreadIns
=
threading.Thread(target
=
task)
subThreadIns.start()
eventLock.
set
()
# 设置为绿灯
eventLock.clear()
# 设置为红灯
eventLock.
set
()
# 设置为绿灯
# start and wait run thread : Thread-1
# start and wait run thread : Thread-2
# start and wait run thread : Thread-3
# green light, Thread-1 carry on run
# red light, Thread-1 stop run
# green light, Thread-1 carry on run
# sub thread Thread-1 run end
# green light, Thread-3 carry on run
# red light, Thread-3 stop run
# green light, Thread-3 carry on run
# sub thread Thread-3 run end
# green light, Thread-2 carry on run
# red light, Thread-2 stop run
# green light, Thread-2 carry on run
# sub thread Thread-2 run end
|
基本介绍 。
信号量锁也是根据条件锁来做的,它与条件锁和事件锁的区别如下:
下面是threading模块与信号量锁提供的相关方法:
方法 | 描述 |
---|---|
threading.Semaphore() | 返回一个信号量锁对象 |
lockObject.acquire(blocking=True, timeout=1) | 上锁,当一个线程在执行被上锁代码块时,将不允许切换到其他线程运行,默认锁失效时间为1秒 |
lockObject.release() | 解锁,当一个线程在执行未被上锁代码块时,将允许系统根据策略自行切换到其他线程中运行 |
以下是使用示例,你可以将它当做一段限宽的路段,每次只能放行相同数量的线程:
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
maxSubThreadNumber
=
6
def
task():
thName
=
threading.currentThread().name
semaLock.acquire()
print
(
"run sub thread %s"
%
thName)
time.sleep(
3
)
semaLock.release()
if
__name__
=
=
"__main__"
:
# 每次只能放行2个
semaLock
=
threading.Semaphore(
2
)
for
i
in
range
(maxSubThreadNumber):
subThreadIns
=
threading.Thread(target
=
task)
subThreadIns.start()
# run sub thread Thread-1
# run sub thread Thread-2
# run sub thread Thread-3
# run sub thread Thread-4
# run sub thread Thread-6
# run sub thread Thread-5
|
由于threading.Semaphore()对象中实现了__enter__()与__exit__()方法,故我们可以使用with语句进行上下文管理形式的加锁解锁操作:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
import
threading
import
time
maxSubThreadNumber
=
6
def
task():
thName
=
threading.currentThread().name
with semaLock:
print
(
"run sub thread %s"
%
thName)
time.sleep(
3
)
if
__name__
=
=
"__main__"
:
semaLock
=
threading.Semaphore(
2
)
for
i
in
range
(maxSubThreadNumber):
subThreadIns
=
threading.Thread(target
=
task)
subThreadIns.start()
|
上面5种锁可以说都是基于同步锁来做的,这些你都可以从源码中找到答案.
首先来看RLock递归锁,递归锁的实现非常简单,它的内部会维护着一个计数器,当计数器不为0的时候该线程不能被I/O操作和时间轮询机制切换。但是当计数器为0的时候便不会如此了:
1
2
3
4
|
def
__init__(
self
):
self
._block
=
_allocate_lock()
self
._owner
=
None
self
._count
=
0
# 计数器
|
而Condition条件锁的内部其实是有两把锁的,一把底层锁(同步锁)一把高级锁(递归锁).
低层锁的解锁方式有两种,使用wait()方法会暂时解开底层锁同时加上一把高级锁,只有当接收到别的线程里的notfiy()后才会解开高级锁和重新上锁低层锁,也就是说条件锁底层是根据同步锁和递归锁的不断切换来进行实现的:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
def
__init__(
self
, lock
=
None
):
if
lock
is
None
:
lock
=
RLock()
# 可以看到条件锁的内部是基于递归锁,而递归锁又是基于同步锁来做的
self
._lock
=
lock
self
.acquire
=
lock.acquire
self
.release
=
lock.release
try
:
self
._release_save
=
lock._release_save
except
AttributeError:
pass
try
:
self
._acquire_restore
=
lock._acquire_restore
except
AttributeError:
pass
try
:
self
._is_owned
=
lock._is_owned
except
AttributeError:
pass
self
._waiters
=
_deque()
|
Event事件锁内部是基于条件锁来做的:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
class
Event:
def
__init__(
self
):
self
._cond
=
Condition(Lock())
# 实例化出了一个条件锁。
self
._flag
=
False
def
_reset_internal_locks(
self
):
# private! called by Thread._reset_internal_locks by _after_fork()
self
._cond.__init__(Lock())
def
is_set(
self
):
"""Return true if and only if the internal flag is true."""
return
self
._flag
isSet
=
is_set
|
Semaphore信号量锁内部也是基于条件锁来做的:
1
2
3
4
5
6
7
|
class
Semaphore:
def
__init__(
self
, value
=
1
):
if
value <
0
:
raise
ValueError(
"semaphore initial value must be >= 0"
)
self
._cond
=
Condition(Lock())
# 可以看到,这里是实例化出了一个条件锁
self
._value
=
value
|
需求:一个空列表,两个线程轮番往里面加值(一个加偶数,一个加奇数),最终让该列表中的值为 1 - 100 ,且是有序排列的.
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
45
46
47
48
49
|
import
threading
lst
=
[]
def
even():
"""加偶数"""
with condLock:
for
i
in
range
(
2
,
101
,
2
):
# 判断当前列表的长度处于2是否能处尽
# 如果能处尽则代表需要添加奇数
# 否则就添加偶数
if
len
(lst)
%
2
!
=
0
:
# 添偶数
lst.append(i)
# 先添加值
condLock.notify()
# 告诉另一个线程,你可以加奇数了,但是这里不会立即交出执行权
condLock.wait()
# 交出执行权,并等待另一个线程通知加偶数
else
:
# 添奇数
condLock.wait()
# 交出执行权,等待另一个线程通知加偶数
lst.append(i)
condLock.notify()
condLock.notify()
def
odd():
"""加奇数"""
with condLock:
for
i
in
range
(
1
,
101
,
2
):
if
len
(lst)
%
2
=
=
0
:
lst.append(i)
condLock.notify()
condLock.wait()
condLock.notify()
if
__name__
=
=
"__main__"
:
condLock
=
threading.Condition()
addEvenTask
=
threading.Thread(target
=
even)
addOddTask
=
threading.Thread(target
=
odd)
addEvenTask.start()
addOddTask.start()
addEvenTask.join()
addOddTask.join()
print
(lst)
|
有2个任务线程来扮演李白和杜甫,如何让他们一人一句进行对答?文本如下:
杜甫:老李啊,来喝酒! 。
李白:老杜啊,不喝了我喝不下了! 。
杜甫:老李啊,再来一壶?
杜甫:...老李?
李白:呼呼呼...睡着了.. 。
代码如下:
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
def
libai():
event.wait()
print
(
"李白:老杜啊,不喝了我喝不下了!"
)
event.
set
()
event.clear()
event.wait()
print
(
"李白:呼呼呼...睡着了.."
)
def
dufu():
print
(
"杜甫:老李啊,来喝酒!"
)
event.
set
()
event.clear()
event.wait()
print
(
"杜甫:老李啊,再来一壶?"
)
print
(
"杜甫:...老李?"
)
event.
set
()
if
__name__
=
=
'__main__'
:
event
=
threading.Event()
t1
=
threading.Thread(target
=
libai)
t2
=
threading.Thread(target
=
dufu)
t1.start()
t2.start()
t1.join()
t2.join()
|
到此这篇关于Python3中最常用的5种线程锁的文章就介绍到这了,更多相关Python3常用线程锁内容请搜索我以前的文章或继续浏览下面的相关文章希望大家以后多多支持我! 。
原文链接:https://www.cnblogs.com/YunyaSir/p/14963682.html 。
最后此篇关于Python3中最常用的5种线程锁实例总结的文章就讲到这里了,如果你想了解更多关于Python3中最常用的5种线程锁实例总结的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
有没有一种方法可以使用标准类型构造函数(例如 int、set、dict、list、tuple 等)以用户定义的方式将用户定义类的实例强制转换为其中一种类型?例如 class Example:
我知道这个问题在Stackoverflow中有很多问题,但是即使有很多答案,这些答案也帮不了我什么,也没有找到答案。 在我的WebAPP中,它可以正常工作,但是当我将其转换为API时,它失败了(主题标
这个问题已经有答案了: Why does the ternary operator unexpectedly cast integers? (3 个回答) 已关闭 9 年前。 最近遇到一个Java的陷
我尝试使用 FirebaseApp.configure() 配置 Firebase,但遇到以下崩溃: *** Terminating app due to uncaught exception 'c
我有一个自连接员工实体类,其中包含与其自身相关的 id、name 和 ref 列。我想创建它的新实例并将其保存到数据库。 首先我创建了一个 Employee 类的实例并将其命名为 manager。然后
我有一个用于添加新公寓的表单,在该表单中我有一个下拉列表,用户可以在其中选择负责的人员。 显然,当您从下拉列表中选择并尝试保存公寓时,我的应用程序认为该人已被修改。它给了我下面的错误,指示我应该首先保
从 Visualforce 页面,我需要检索我们组织的 salesforce 实例的 URL,而不是 Visual Force URL。 例如我需要https://cs1.salesforce.com
我遇到了一些可能的问题答案,但这是关于从 Hibernate 3.4.0GA 升级到 Hibernate 4.1.8 的问题。所以这曾经在以前的版本下工作,我已经四处搜索了为什么它在这个新版本中出现了
似乎一遍又一遍地问这个问题,我仍然找不到解决我问题的答案。我在下面有一个域模型。每个新创建或更新的“安全用户”都需要我确保其具有配置文件,如果没有,则创建一个新的配置文件并分配给它。 配置文件的要求相
我很难调试为什么 JPA 不级联我的 @ManyToMany 关系。我发现的所有答案都与缺少级联语句有关。但我确实拥有它们并且仍然得到: Caused by: org.hibernate.Transi
Play 服务 API 表明有一个叫做 Instance ID 的东西 但是,在 Android Studio 中包含以下内容后,我无法导入 InstanceID 类 compile "com.goo
我正在使用 Seam 框架。我有 2 个实体: 请求.java @Entity @Table(name = "SRV_REQUEST") public class Request { private
This question处理构建一个适当的Monad来自单子(monad)的实例,但仅在某些约束下 - 例如Set .诀窍是将其包装成 ContT ,它将约束推迟到包装/展开其值。 现在我想对 Ap
我正在尝试执行此查询: StringBuffer sb = new StringBuffer(); sb.append("select p from PointsEntity p " + "where
我试图了解是否可以更改我的 hibernate 配置并使用单个 MySQL 实例(而不是我当前拥有的多个 MySQL 实例): 我有一个使用 hibernate 的 Java 应用程序,与 2 个模式
我有一个选项卡滑动布局,其中包括四个选项卡,每个选项卡都有自己的布局和 fragment ,在我的主要 Activity 布局中,viewpager 参与更改选项卡。特定 View (选项卡)在应用程
我看到很多帖子声称他们正在运行 MySql 的 RDS 实例,但无法连接到该实例,但我没有运行 RDS。 我使用 EC2 实例来托管我的 WordPress 博客,该博客是使用 Web 平台安装程序安
因为我在我的 ec-2 实例上的 python 虚拟环境中运行应用程序( Airflow ),并且我想在同一个 ec2 实例上的默认 python 环境中运行命令,所以我认为 ssh 到我自己的实例更
这个问题已经有答案了: How to fix the Hibernate "object references an unsaved transient instance - save the tra
例子: run APP1 .. ... run APP1 ... run APP2 如何在 APP2 中对 Vue 说我需要调用 APP1?
我是一名优秀的程序员,十分优秀!