创建多线程
- 方法一:使用threading.Thread类 ```python import threading import time
def sing(): for i in range(3): print(‘%d is singing’ % i) time.sleep(2)
def dance(): for i in range(3): print(‘%d is dancing’ % i) time.sleep(1)
if name == ‘main‘: t1 = threading.Thread(target=sing) t2 = threading.Thread(target=dance) t1.start() t2.start()
- 方式二:继承threading.Thread类,重写run()方法
```python
import threading
import time
class SingThread(threading.Thread):
def run(self):
for i in range(3):
print('%d is singing' % i)
time.sleep(2)
class DanceThread(threading.Thread):
def run(self):
for i in range(3):
print('%d is dancing' % i)
time.sleep(1)
if __name__ == '__main__':
t1 = SingThread()
t2 = DanceThread()
t1.start()
t2.start()
其他说明
方式一:在函数内用global声明全局变量 ```python import threading import time
g_num = 100
def work1(): global g_num for i in range(3): g_num += 1 print(‘在线程一执行之后,g_num = %d’ % g_num)
def work2(): time.sleep(1) print(‘在线程二执行之后,g_num = %d’ % g_num)
if name == ‘main‘: print(‘在线程创建之前,g_num = %d’ % g_num) t1 = threading.Thread(target=work1) t2 = threading.Thread(target=work2) t1.start() t2.start() t1.join() t2.join() print(‘在线程执行之后,g_num = %d’ % g_num)
- 方式二:传递可变参数到函数中
```python
import threading
import time
g_num = [1]
def work1(num):
for i in range(3):
num.append(i)
print('在线程一执行之后,g_num = %s' % num)
def work2(num):
time.sleep(1)
print('在线程二执行之后,g_num = %s' % num)
if __name__ == '__main__':
print('在线程创建之前,g_num = %s' % g_num)
t1 = threading.Thread(target=work1, args=(g_num,))
t2 = threading.Thread(target=work2, args=(g_num,))
t1.start()
t2.start()
t1.join()
t2.join()
print('在线程执行之后,g_num = %s' % g_num)
可能遇到的问题
from threading import Thread
g_num = 0
def work1():
global g_num
for i in range(1000000):
g_num += 1
def work2():
global g_num
for i in range(1000000):
g_num += 1
if __name__ == '__main__':
print('在线程创建之前,g_num = %d' % g_num)
t1 = Thread(target=work1)
t2 = Thread(target=work2)
t1.start()
t2.start()
t1.join()
t2.join()
print('在线程执行之后,g_num = %d' % g_num)
多线程的同步
- 当多个线程几乎同时修改某一个共享数据的时候,需要进行同步控制。同步就是协同步调,按预定的先后次序执行,线程同步能够保证多个线程安全访问共享数据。最简单的同步机制是引入互斥锁
- 互斥锁为共享数据引入一对状态:锁定 / 非锁定。当某个线程要更改共享数据时,先将其锁定,此时共享数据的状态为”锁定”,其他线程不能更改。直到该线程释放该共享数据,将该共享数据的状态变成”非锁定”,其他的线程才能再次锁定该共享数据。
- 互斥锁保证了每次只有一个线程进行写入操作,从而保证了多线程情况下共享数据的正确性
- threading模块中定义了Lock类,可以方便的处理锁定
- 创建锁:mutex = threading.Lock()
- 上锁:mutex.acquire()
- 解锁:mutex.release()
- 说明:如果这个锁之前是没有上锁的,那么acquire不会堵塞;如果这个锁之前已经被其他线程上了锁,那么acquire会堵塞,直到这个锁被解锁为止
- 锁的好处:确保了某段关键代码只能由一个线程从头到尾完整执行
- 锁的坏处
- 阻止了多线程并发执行,包含锁的某段代码实际上只能以单线程模式执行,效率就大大地下降了
- 由于可以存在多个锁,不同的线程持有不同的锁,并试图获取对方特有的锁时,可能会造成死锁 ```python from threading import Thread, Lock
g_num = 0
def work1(mutex: Lock): global g_num for i in range(1000000): mutex.acquire() g_num += 1 mutex.release()
def work2(mutex: Lock): global g_num for i in range(1000000): mutex.acquire() g_num += 1 mutex.release()
if name == ‘main‘: mutex = Lock() t1 = Thread(target=work1, args=[mutex, ]) t2 = Thread(target=work2, args=[mutex, ]) t1.start() t2.start() t1.join() t2.join() print(g_num)
```python
from threading import Thread, Lock
g_num = 0
mutex = Lock()
def work1():
global g_num
for i in range(1000000):
mutex.acquire()
g_num += 1
mutex.release()
def work2():
global g_num
for i in range(1000000):
mutex.acquire()
g_num += 1
mutex.release()
if __name__ == '__main__':
t1 = Thread(target=work1)
t2 = Thread(target=work2)
t1.start()
t2.start()
t1.join()
t2.join()
print(g_num)
死锁
- 在线程间共享多个资源的时候,如果两个线程分别占有一部分资源并且同时等待对方的资源,就会造成死锁
- 避免死锁
- 程序设计时要尽量避免(银行家算法)
- 添加超时时间 ```python import threading import time
mutexA = threading.Lock() mutexB = threading.Lock()
class MyThread1(threading.Thread): def run(self): mutexA.acquire() time.sleep(1)
print('apply for mutexB')
mutexB.acquire()
print('release mutexB')
mutexB.release()
mutexA.release()
class MyThread2(threading.Thread): def run(self): mutexB.acquire() time.sleep(1)
print('apply for mutexA')
mutexA.acquire()
print('release mutexA')
mutexA.release()
mutexB.release()
if name == ‘main‘: t1 = MyThread1() t2 = MyThread2() t1.start() t2.start() ```