创建多线程

  • 方法一:使用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()

  1. - 方式二:继承threading.Thread类,重写run()方法
  2. ```python
  3. import threading
  4. import time
  5. class SingThread(threading.Thread):
  6. def run(self):
  7. for i in range(3):
  8. print('%d is singing' % i)
  9. time.sleep(2)
  10. class DanceThread(threading.Thread):
  11. def run(self):
  12. for i in range(3):
  13. print('%d is dancing' % i)
  14. time.sleep(1)
  15. if __name__ == '__main__':
  16. t1 = SingThread()
  17. t2 = DanceThread()
  18. t1.start()
  19. t2.start()
  • 其他说明

    • Linux命令行查看线程的方法
      • 方法一:ps -elLf | grep 线程名字
      • 方法二:top,然后按 H 来查看线程
    • 当调用start()时,才会真正的创建线程,并且开始执行
    • 代码中查看当前程序中的线程数量:len(threading.enumerate())
    • 多线程程序的执行顺序是不确定的,调度程序将自行选择就绪队列中的一个线程执行
    • 主线程中执行exit(),只是主线程结束了,子线程还在继续执行
    • 子线程名.join():主线程会等待该子线程执行完,才往下执行

      多线程中的全局变量

      共享全局变量

  • 方式一:在函数内用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() ```