Python 多任务 —— 线程(锁)

高级编程技巧 学习笔记

一、共享全局变量资源竞争


  • 一个线程写入,一个线程读取,没问题,如果两个线程都写入呢?
import threading


def demo1(nums):
    global num
    for i in range(nums):
        num += 1
    print('demo1---num:%d' % num)


def demo2(nums):
    global num
    for i in range(nums):
        num += 1
    print('demo2--num:%d' % num)


def main():
    t1 = threading.Thread(target=demo1, args=(100,))
    t2 = threading.Thread(target=demo2, args=(100,))

    t1.start()
    t2.start()

    print("main----num:%d" % num)


if __name__ == '__main__':
    num = 0
    main()
  • 当 num=100 时,执行结果与我们预想的一样(100,200,200)
  • 当 num=1000000 很大时,结果却有出入

这就是 “共享全局变量资源竞争” 引起的问题。

	# 代码 num += 1 执行时, 其实是分成三个语句执行的
		1.获取 num 的值
		2.把获取到的 num +1
		3.把第2步的结果从新赋值给 num
	# cpu 处理线程时是使用时间片轮转的方式
	# 因此, 当线程的时间片用完时 num+=1 代码并未执行完成

如何解决呢?使用互斥锁



二、互斥锁


  • 创建锁 mutex = threading.Lock()

  • 锁定 mutex.acquire()

  • 解锁 mutex.release()

import threading
import time


def demo1(nums):
    global num
    # 加锁
    mutex.acquire()
    for i in range(nums):
        num += 1
    # 解锁
    mutex.release()
    print('demo1---num:%d' % num)


def demo2(nums):
    global num
    # 加锁
    mutex.acquire()
    for i in range(nums):
        num += 1
    # 解锁
    mutex.release()
    print('demo2--num:%d' % num)


def main():
    t1 = threading.Thread(target=demo1, args=(1000000,))
    t2 = threading.Thread(target=demo2, args=(1000000,))

    t1.start()
    t2.start()

    # time.sleep(1)
    print("main----num:%d" % num)


if __name__ == '__main__':
	num = 0
	# 创建锁
	mutex = threading.Lock()
    main()


# main----num:164353
# demo1---num:1000000
# demo2--num:2000000

为什么 main 打印出来的结果还是不对??

因为子线程执行的同时,主进程也再继续执行,所以 main 打印出来的值有可能是 demo2 执行过程中的某一个 num 值,而不是最终的 num 值。

取消上面的 time.sleep(1) 注释在执行,对比结果。(注循环的次数越大,则等待的时间这越长)



三、死锁


3.1、死锁实例

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

class MyThread1(threading.Thread):
    def run(self):
        # 对mutexA上锁
        mutexA.acquire()

        # mutexA上锁后,延时1秒,这时另外那个线程已经把mutexB上锁
        print(self.name+'----do1---up----')
        time.sleep(1)

        # 此时会堵塞,因为这个mutexB已经被另外的线程抢先上锁了,未解锁前不能重新上锁
        mutexB.acquire()
        print(self.name+'----do1---down----')
        mutexB.release()

        # 对mutexA解锁
        mutexA.release()


class MyThread2(threading.Thread):
    def run(self):
        # 对mutexB上锁
        mutexB.acquire()

        # mutexB上锁后,延时1秒,这时另外那个线程 把mutexA上锁
        print(self.name+'----do2---up----')
        time.sleep(1)

        # 此时会堵塞,因为这个mutexA已经被另外的线程抢先上锁了,未解锁前不能重新上锁
        mutexA.acquire()
        print(self.name+'----do2---down----')
        mutexA.release()

        # 对mutexB解锁
        mutexB.release()


if __name__ == '__main__':
    mutexA = threading.Lock()
    mutexB = threading.Lock()
    t1 = MyThread1()
    t2 = MyThread2()
    t1.start()
    t2.start()

3.2、避免死锁

  • 程序设计时要尽量避免

  • 添加超时时间等

发布了85 篇原创文章 · 获赞 0 · 访问量 1217

猜你喜欢

转载自blog.csdn.net/qq_43621629/article/details/104100773
今日推荐