多线程

线程

python的thread模块是比较底层的模块,python的threading模块是对thread做了一些包装的,可以更加方便的被使用

使用threading模块

# coding=utf-8
import threading
import time


def sing():
    for _ in range(10):
        print("正在唱歌")
        time.sleep(1)
        
   
def dance():
    for _ in range(10):
        print("正在跳舞")
        time.sleep(1)
        

def main():
    t1 = threading.Thread(target=sing)
    t2 = threading.Thread(target=sing)
    t1.start()
    t2.start()
    
    
if __name__ == "__main__":
    main()

查看线程数量

# coding=utf-8
import threading
from time import sleep,ctime


def sing():
    for _ in range(3):
        print("正在唱歌")
        sleep(1)
        
        
def dance():
    for _ in range(3):
        print("正在跳舞")
        sleep(1)

if __name__ == "__main__":
    print("---开始---:%" % ctime())
    
    t1 = threading.Thread(target=sing)
    t2 = threading.Thread(target=dance)
    
    t1.start()
    t2.start()
    
    while True:
        length = len(threading.enumerate())
        print('当前运行的线程数为:%d' % length)
        if length <= 1:
            break
        sleep(0.5)

线程-注意点

1.线程执行代码的封装

通过使用threading模块能够完成多任务的程序开发,为了让每个线程的封装性更完美,所以使用threading模块时,往往会定义一个新的子类class,只要继承threading.Tread 就可以了,然后重写run方法。这种方式可以在一个线程里面执行多个函数,其他函数放在run里面执行

#coding=utf-8
import htreading
import time


class MyThread(threading.Thread):
    def run(self):
        for i in range(3):
            time.sleep(1)
            msg = "I'm" + self.name + '@' + str(i)  # name属性中保存的是当前线程的名字
            print(msg)
            
      
if __name__ == "__main__":
    t = MyThread()
    t.start()  # 会执行类里面run()方法,如果没有run方法,则运行出错

多线程-全局共享变量

from threading import Thread
import time

g_num = 0

def work1():
    global g_num
    for i in range(3):
        g_num += 1
    print("---in work1,gum is %d---" % g_num)
    
    
def work2():
    global g_num
	print("---in work2,g_num is %d" % g_num)

print("---线程创建之前g_num is %d" % g_num)

t1 = Thread(target=work1)
t1.start()
time.sleep(1)

t2 = Thread(target=work2)
t2.start()

列表当做实参传递到线程中

from threading import Thread
import time


def work1(nums):
    nums.append(44)
    print("--in work1--", nums)
    

def work2(nums):
    # 延迟一会,保证t1线程中的事情做完
    time.sleep(1)
    print("--in work2--", nums)
    
    
g_nums = [11, 22, 33]
t1 = Thread(target=work1, args=(g_nums,))
t1.start()

t2 = Thread(target=work2, args=(g_nums,))
t2.start()

多线程-共享全局变量问题

多线程开发可能遇到的问题

假设两个线程t1和t2都要对全局变量g_num(默认是0)进行加1运算,t1和t2都各对g_num加10次,g_num的最终的结果应该为20

但是由于多线程的同时操作,有可能出现下面情况:

1.在g_num=0时,t1取得g_num=0.此时系统把t1调度为"sleeping"状态,把t2转换为"running"状态,t2也获得g_num=0

2.然后t2对得到的值进行加1并赋给g_num,使得g_num=1

3.然后系统又把t2调度为"sleeping",把t1转为"running".线程t1又把它之前得到的0加1后赋值给g_num.

4.这样导致虽然t1和t2都对g_num加1,但结果仍然是g_num=1

同步的概念

同步就是协同步调,按预定的先后次序进行运行

解决线程同时修改全局变量的方式

  1. 系统调用t1,然后获取到g_num的值为0,此时上一把锁,既不允许其他线程操作g_num
  2. t1对g_num的值进行+1
  3. t1解锁,此时g_num的值为1,其他线程就可以使用g_num了,而且是g_num的值不是0,而是1
  4. 同理其他线程在对g_num进行修改时,都要先上锁,处理完后再解锁,在上锁的整个过程中不允许其他线程访问,既保证了数据的正确性

互斥锁

import threading

# 创建锁
mutex = threading.Lock()

# 锁定
mutex.acquire()

# 释放
mutex.release()

'''  注意:
如果这个锁之前是没有上锁的,那么acquire不会堵塞
如果在调用acquire对这个锁上锁之前,它就已经被其他线程上了锁,囊额此时acquire会堵塞,知道这个锁被解锁为止
上锁的代码越少越好
'''

示例:
import threading


g_num = 0
mutex = threading.Lock()

def work1():
    global g_num
    for _ in range(1000000):
        mutex.acquire()
        g_num += 1
        mutex.release()
        
        
def work2():
    global g_num
    for _ in range(1000000):
        mutex.acquire()
        g_num += 1
        mutex.release()
        
def main():
    t1 = threading.Thread(target=work1)
    t2 = threading.Thread(target=work2)
    
    t1.start()
    t2.start()
    
    print(g_num)
    
if __name__ == "__main__":
    main()

死锁

在线程间共享多个资源的时候,如果两个线程分别占有一部分资源并且同时等待对方的资源,就会造成死锁

避免死锁

  • 程序设计是要尽量避免(银行家算法)
  • 添加超时时间等

posted on 2021-03-28 22:16  C_yuyan  阅读(67)  评论(0)    收藏  举报