0%

python多线程

线程对象:threading.Thread

threading.Thread(group=None, target=None, name=None, args=(), kwargs={}, *, daemon=None)

  • traget:该线程要调用的函数
  • args:该线程要传入的参数,元组形式,默认为空元组,可以和kwargs一起使用
  • kwargs:该线程要传入的参数,字典形式

方法:

  • sart():开启一个线程,使run()方法在一个线程中被调用

  • run():主要用去创建子类时重写

  • join():确保调用该方法的线程执行完之后再执行下一个线程

  • is_alive():判断该线程是否存活

  • daemon():判断该线程是否是守护线程。所有在主线程中创建的线程默认都是非守护线程。

补充:

  • 守护线程:只有当所有守护线程都结束,python程序才会退出。若python程序退出,那么所有的守护线程都会被终止,从而结束程序
  • 非守护线程:当python程序结束时,若还有非守护线程在运行,那么会等到所有非守护线程结束才会退出

示例:

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
"""
通过继承threading.Thread的子类创建线程
"""
import time
import threading


class TestThread(threading.Thread):
def __init__(self, para='hi', sleep=3):
# 重写threading.Thread的__init__方法时,确保在所有操作之前先调用threading.Thread.__init__方法
super().__init__()
self.para = para
self.sleep = sleep

def run(self):
"""线程内容"""
time.sleep(self.sleep)
print(self.para)


def main():
# 创建线程
thread_hi = TestThread()
thread_hello = TestThread('hello', 1)
# 启动线程
thread_hi.start()
thread_hello.start()
print('Main thread has ended!')


if __name__ == '__main__':
main()
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
"""
使用join方法阻塞主线程
"""
import time
import threading


def test_thread(para='hi', sleep=5):
"""线程运行函数"""
time.sleep(sleep)
print(para)


def main():
# 创建线程
thread_hi = threading.Thread(target=test_thread)
thread_hello = threading.Thread(target=test_thread, args=('hello', 1))
# 启动线程
thread_hi.start()
thread_hello.start()
time.sleep(2)
print('马上执行join方法了')
# 执行join方法会阻塞调用线程(主线程),直到调用join方法的线程(thread_hi)结束
thread_hi.join()
print('线程thread_hi已结束')
# 这里不会阻塞主线程,因为运行到这里的时候,线程thread_hello已经运行结束了
thread_hello.join()
print('Main thread has ended!')

# 以上代码只是为了展示join方法的效果
# 如果想要等所有线程都运行完成后再做其他操作,可以使用for循环
# for thd in (thread_hi, thread_hello):
# thd.join()
#
# print('所有线程执行结束后的其他操作')


if __name__ == '__main__':
main()

  • 锁只有锁定与非锁定两种状态
  • 当一个锁被锁定时,它并不属于某一个线程
  • 锁被创建时,处于非锁定状态,使用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
"""
使用锁实现线程同步
"""
import time
import threading

# 创建锁
lock = threading.Lock()

# 全局变量
global_resource = [None] * 5


def change_resource(para, sleep):
# 请求锁
lock.acquire()

# 这段代码如果不加锁,第一个线程运行结束后global_resource中是乱的,输出为:修改全局变量为: ['hello', 'hi', 'hi', 'hello', 'hello']
# 第二个线程运行结束后,global_resource中还是乱的,输出为:修改全局变量为: ['hello', 'hi', 'hi', 'hi', 'hi']
# 若想在函数内部对函数外的变量进行操作,就需要在函数内部声明其为global
global global_resource
for i in range(len(global_resource)):
global_resource[i] = para
time.sleep(sleep)
print("修改全局变量为:", global_resource)

# 释放锁
lock.release()


def main():
thread_hi = threading.Thread(target=change_resource, args=('hi', 2))
thread_hello = threading.Thread(target=change_resource, args=('hello', 1))
thread_hi.start()
thread_hello.start()


if __name__ == '__main__':
main()

递归锁

  • 释放递归锁的操作必须由获得该锁的线程来进行(同一递归等级)
  • 同一个线程在释放锁之前再次获得锁不会阻塞该线程,而是将递归等级加一(初始递归等级为1)
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
"""
在普通锁中可能造成死锁的情况,可以考虑使用递归锁解决
"""
import time
import threading


# 如果是使用的两个普通锁,那么就会造成死锁的情况,程序一直阻塞而不会退出
# rlock_hi = threading.Lock()
# rlock_hello = threading.Lock()

# 使用成一个递归锁就可以解决当前这种死锁情况
rlock_hi = rlock_hello = threading.RLock()


def test_thread_hi():
# 初始时锁内部的递归等级为1
rlock_hi.acquire()
print('线程test_thread_hi获得了锁rlock_hi')
time.sleep(2)
# 如果再次获取同样一把锁,则不会阻塞,只是内部的递归等级加1
rlock_hello.acquire()
print('线程test_thread_hi获得了锁rlock_hello')
# 释放一次锁,内部递归等级减1
rlock_hello.release()
# 这里再次减,当递归等级为0时,其他线程才可获取到此锁
rlock_hi.release()


def test_thread_hello():
rlock_hello.acquire()
print('线程test_thread_hello获得了锁rlock_hello')
time.sleep(2)
rlock_hi.acquire()
print('线程test_thread_hello获得了锁rlock_hi')
rlock_hi.release()
rlock_hello.release()


def main():
thread_hi = threading.Thread(target=test_thread_hi)
thread_hello = threading.Thread(target=test_thread_hello)
thread_hi.start()
thread_hello.start()


if __name__ == '__main__':
main()

条件变量对象

补充:

1
2
3
4
5
6
7
8
>>> print(True == 1)
>>> print(True == 2)
>>> print(False == 0)
>>> print(False == 2)
True
False
True
False

threading.Condition(lock=None):一个条件变量对象允许一个或多个线程等待,直到被另一个线程通知

方法:

  • wait(timeout=None):释放锁,等待直到被通知或超时,等待的时间内阻塞线程。
  • wait_for(predicate,timeout=None):与wait方法类似,等待。这个方法首先调用predicate函数,判断其返回值,若为true,释放锁,并阻塞,待被通知或超时后,调用predicate函数,返回true,则获得锁,不再阻塞;若为false,继续阻塞;若为false,则不会释放锁,程序继续执行。
  • notify(n=1):唤醒n个正在等待这个条件变量的线程
  • notify_all():
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
"""
让一个线程等待,直到另一个线程通知
"""
import time
import threading


# 创建条件变量对象
condition_lock = threading.Condition()

PRE = 0


# predicate可调用函数
def pre():
print(PRE)
return PRE


def test_thread_hi():
# 在使用wait/wait_for之前必须先获得锁
condition_lock.acquire()

print('等待线程test_thread_hello的通知')
# 先执行一次pre,返回False后释放掉锁,等另一个线程释放掉锁后再次执行pre,返回True后再次获取锁
# wait_for的返回值不是True和False,而是predicate参数的返回值
condition_lock.wait_for(pre)
# condition_lock.wait()
print('继续执行')

# 不要忘记使用wait/wait_for之后要释放锁
condition_lock.release()


def test_thread_hello():
time.sleep(1)
condition_lock.acquire()

global PRE
PRE = 1
print('修改PRE值为1')

print('通知线程test_thread_hi可以准备获取锁了')
condition_lock.notify()

# 先notify/notify_all之后在释放锁
condition_lock.release()
print('你获取锁吧')


def main():
thread_hi = threading.Thread(target=test_thread_hi)
thread_hello = threading.Thread(target=test_thread_hello)
thread_hi.start()
thread_hello.start()


if __name__ == '__main__':
main()