Python多线程,线程死锁及解决,生产者与消费者问题
1.Thread类
普通调用
t = Thread(target=test, args=(i,))	# test为目标函数名, 若函数需要参数将其以元组形									                # 式赋给args, 若无参数可不写
t.start()	# 用start()函数开启线程
例子
import time
from threading import Thread
# 目标函数
def test(i):
    print("hello ", i)
    time.sleep(1)
def main():
    # 循环5次,开起五个线程
    for i in range(5):
        t = Thread(target=test, args=(i,))
        t.start()
if __name__ == '__main__':
    main()
继承Thread类
定义一个自己的类继承自Thread,重写run()方法,即 将原本执行任务的函数内容移植到run()方法中.可通过类的属性传参.
例子
from threading import Thread
import time
class MyThread(Thread):
    def __init__(self, i):
        Thread.__init__(self)   # 初始化父类"构造函数"
        self.i = i  # 初始化,目的将run函数参数作为类的属性
    def run(self):
        time.sleep(1)
        msg = "I'm " + self.name + " @ " + str(self.i)
        print(msg)
def main():
    for i in range(3):	# 开启三个线程
        t = MyThread(i)	# 实例化自己的类
        t.start()
if __name__ == '__main__':
    main()
线程的执行顺序
上面的例子中线程的执行顺序是随机的
2.线程间共享全局变量
下面例子中test1()和test2()共享g_num全局变量.希望test1()执行的结果是1000000,test2()执行的结果是2000000.但是time.sleep()函数会影响结果.
from threading import Thread
import time
g_num = 0
def test1():
    global g_num
    for i in range(1000000):
        g_num += 1
    print('---test1 g_num is %d---' % g_num)
def test2():
    global g_num
    for i in range(1000000):
        g_num += 1
    print('---test2 g_num is %d---' % g_num)
t1 = Thread(target=test1)
t1.start()
# time.sleep(3) # 运行这句话与不运行这句话结果不一样
t2 = Thread(target=test2)
t2.start()
print('-----g_num: %d-----' % g_num)
不执行sleep函数的结果(可能出现多种不同的运行结果)
---test1 g_num is 1312283---
-----g_num: 1312283-----
---test2 g_num is 1341534---
执行sleep()函数的结果
---test1 g_num is 1000000---
-----g_num: 1079982-----
---test2 g_num is 2000000---
其实这也不难理解,sleep之后test1中的任务肯定先完成,而不执行sleep两个函数同能对g_num同时操作
通过轮询的方式解决线程间共享全局变量的问题
from threading import Thread
g_num = 0
g_flag = 1  # 增加一个标识全局变量
def test1():
    global g_num
    global g_flag
    if g_flag == 1:
        for i in range(1000000):
            g_num += 1
    g_flag = 0
    print('---test1 g_num is %d---' % g_num)
def test2():
    global g_num
    # 轮询
    while True:
        if g_flag != 1: # 一旦test1()执行完,即g_flag = 0时,test2()开始执行累加g_num操作
            for i in range(1000000):
                g_num += 1
            break
    print('---test2 g_num is %d---' % g_num)
t1 = Thread(target=test1)
t1.start()
t2 = Thread(target=test2)
t2.start()
print('-----g_num: %d-----' % g_num)
运行结果
-----g_num: 303721-----
---test1 g_num is 1000000---
---test2 g_num is 2000000---
第二个线程一开始并没有执行累加g_num的操作,而是先进行一个死循环,在这个循环中不断的"询问"g_flag的值是否不等于1.一但g_flag不等于1,即test1()结束后便开始干"正事".
通过互斥锁解决线程间共享全局变量的问题
from threading import Thread, Lock  # 导入互斥锁
g_num = 0
def test1():
    global g_num
    for i in range(1000000):
        mutex.acquire()  # 上锁,此时其他的锁会等待  上锁应该遵循最小原则
        g_num += 1
        mutex.release() # 开锁,此时其他的锁会抢着开锁
    print('---test1 g_num is %d---' % g_num)
def test2():
    global g_num
    for i in range(1000000):
        mutex.acquire()
        g_num += 1
        mutex.release()
    print('---test2 g_num is %d---' % g_num)
# 创建一把互斥锁,默认不上锁
mutex = Lock()
t1 = Thread(target=test1)
t1.start()
t2 = Thread(target=test2)
t2.start()
print('-----g_num: %d-----' % g_num)
运行结果
-----g_num: 45012-----
---test1 g_num is 1979942---
---test2 g_num is 2000000---
从结果可以看出test2()的结果是正确的,而test1()的结果很接近test2.这也不难理解.互斥锁会把夹在中间的部分锁定,也就是说,在极短时间内只能有一个线程在执行该代码.一旦开锁了(release),所有线程开始抢这把锁,某个线程抢到之后会把自己的操作锁住,其他线程只能等待,一直反复直至全部任务完成.
只有对上述代码稍微修改便可以实现我们想要的结果
修改后的代码
from threading import Thread, Lock  # 导入互斥锁
g_num = 0
def test1():
    global g_num
    mutex.acquire()  # 上锁,此时其他的锁会等待  上锁应该遵循最小原则
    for i in range(1000000):
        g_num += 1
    mutex.release() # 开锁,此时其他的锁会抢着开锁
    print('---test1 g_num is %d---' % g_num)
def test2():
    global g_num
    mutex.acquire()
    for i in range(1000000):
        g_num += 1
    mutex.release()
    print('---test2 g_num is %d---' % g_num)
# 创建一把互斥锁,默认不上锁
mutex = Lock()
t1 = Thread(target=test1)
t1.start()
t2 = Thread(target=test2)
t2.start()
print('-----g_num: %d-----' % g_num)
结果
-----g_num: 220254-----
---test1 g_num is 1000000---
---test2 g_num is 2000000---
值得注意的是,互斥锁上的范围太大就失去了线程的意义,别的线程都把时间浪费在了等待上.轮询同理.
3.线程间使用非全局变量
from threading import Thread
import threading
import time
def test1():
    name = threading.current_thread().name  # 获取当前线程名字
    print('----thread name is %s----' % name)
    g_num = 100
    if name == 'Thread-1':
        g_num += 1
    else:
        time.sleep(2)
    print('---thread is %s | g_num is %d---' % (name, g_num))
t1 = Thread(target=test1)
t1.start()
t2 = Thread(target=test1)
t2.start()
运行结果
----thread name is Thread-1----
---thread is Thread-1 | g_num is 101---
----thread name is Thread-2----
---thread is Thread-2 | g_num is 100---
非全局对于同一个函数来说.可以通过线程的名字来区分.
4.线程死锁
import threading
import time
class MyThread1(threading.Thread):
    def run(self):
        if mutexA.acquire():
            print(self.name + '---do1---up---')
            time.sleep(1)
            if mutexB.acquire():
                print(self.name + '---do1---down---')
                mutexB.release()
            mutexA.release()
class MyThread2(threading.Thread):
    def run(self):
        if mutexB.acquire():
            print(self.name + '---do2---up---')
            time.sleep(1)
            if mutexA.acquire():
                print(self.name + '---do2---down---')
                mutexA.release()
            mutexB.release()
if __name__ == '__main__':
    mutexA = threading.Lock()
    mutexB = threading.Lock()
    t1 = MyThread1()
    t2 = MyThread2()
    t1.start()
    t2.start()
运行结果(卡在了这两句,未结束)
Thread-1---do1---up---
Thread-2---do2---up---
分析代码,t1的代码在等待mutexB解锁的时候t2在等待mutexA解锁.而t1必须先执行完mutexB锁中的代码执行完才能释放mutexA,t2必须先执行完mutexA锁中的代码执行完才能释放mutexB,这就导致两个线程一直等待下去形成死锁,会浪费CPU资源.
解决死锁的办法
设置超时时间 mutexA.acquire(2)
当然也可以从算法上避免死锁
5.使用ThreadLocal
import threading
# 创建全局ThreadLocal对象
local_school = threading.local()
def process_student():
    # 获取当前线程相关联的student
    std = local_school.student
    print('Hello, %s in %s' % (std, threading.current_thread().name))
def process_thread(name):
    # 绑定ThreadLocal的student
    local_school.student = name
    process_student()
t1 = threading.Thread(target=process_thread, args=('kain',), name='Thread-A')
t2 = threading.Thread(target=process_thread, args=('huck',), name='Thread-B')
t1.start()
t2.start()
t1.join()
t2.join()
运行结果
Hello, kain in Thread-A
Hello, huck in Thread-B
6.生产者与消费者问题
import threading
import time
# Python2
# from Queue import Queue
# Python3
from queue import Queue
class Producer(threading.Thread):
    def run(self):
        global queue
        count = 0
        while True:
            if queue.qsize() < 1000:
                for i in range(100):
                    count += 1
                    msg = '生成产品' + str(count)
                    queue.put(msg)
                    print(msg)
            time.sleep(0.5)
class Consumer(threading.Thread):
    def run(self):
        global queue
        while True:
            if queue.qsize() > 100:
                for i in range(3):
                    msg = self.name + '消费了' + queue.get()
                    print(msg)
            time.sleep(1)
if __name__ == '__main__':
    queue = Queue()
    for i in range(500):
        queue.put('初始产品'+str(i))    # 向队列中塞内容
    for i in range(2):
        p = Producer()
        p.start()
    for i in range(5):
        c = Consumer()
        c.start()
运行结果过长不予展示
Python多线程,线程死锁及解决,生产者与消费者问题的更多相关文章
- 多线程-线程间通信-多生产者多消费者问题(JDK1.5后Lock,Condition解决办法及开发中代码范例)
		
1 package multithread4; 2 3 import java.util.concurrent.locks.Condition; 4 import java.util.concurre ...
 - 多线程-线程间通信-多生产者多消费者问题解决(notifyAll)
		
1 package multithread4; 2 3 /* 4 * 生产者,消费者. 5 * 6 * 多生产者,多消费者的问题. 7 * 8 * if判断标记,只有一次,会导致不该运行的线程运行了. ...
 - python 多线程笔记(5)-- 生产者/消费者模式
		
我们已经知道,对公共资源进行互斥访问,可以使用Lock上锁,或者使用RLock去重入锁. 但是这些都只是方便于处理简单的同步现象,我们甚至还不能很合理的去解决使用Lock锁带来的死锁问题. 要解决更复 ...
 - python开发线程:死锁和递归锁&信号量&定时器&线程queue&事件evevt
		
一 死锁现象与递归锁 进程也有死锁与递归锁,在进程那里忘记说了,放到这里一切说了额 所谓死锁: 是指两个或两个以上的进程或线程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将 ...
 - java多线程(同步与死锁问题,生产者与消费者问题)
		
首先我们来看同步与死锁问题: 所谓死锁,就是A拥有banana.B拥有apple. A对B说:你把apple给我.我就把banana给你. B对A说:你把banana给我,我就把apple给你. 可是 ...
 - Python 多线程 线程安全、daemon简介 (四)
		
线程安全 只能在Winodws下的ipython中演示,Python命令行.Pycharm.Mac下的ipython都演示不出效果 import threading def worker(): for ...
 - python多线程编程-queue模块和生产者-消费者问题
		
摘录python核心编程 本例中演示生产者-消费者模型:商品或服务的生产者生产商品,然后将其放到类似队列的数据结构中.生产商品中的时间是不确定的,同样消费者消费商品的时间也是不确定的. 使用queue ...
 - Java多线程-----线程安全及解决机制
		
1.什么是线程安全问题? 从某个线程开始访问到访问结束的整个过程,如果有一个访问对象被其他线程修改,那么对于当前线程而言就发生了线程安全问题: 如果在整个访问过程中,无一对象被其他线程修改,就是线程安 ...
 - java 中多线程之间的通讯之生产者和消费者 (多个线程之间的通讯)
		
在真实开发 中关于多线程的通讯的问题用到下边的例子是比较多的 不同的地方时if 和while 的区别 如果只是两个线程之间的通讯,使用if是没有问题的. 但是在多个线程之间就会有问题 /* * 这个例 ...
 
随机推荐
- Docker 学习之部署php + nginx(一)
			
博主电脑系统是window 10 专业版的,所以在此记录下docker的基本使用方法. 参考地址: https://www.runoob.com/docker/docker-install-php.h ...
 - 记一次 springboot 参数解析 bug调试 HandlerMethodArgumentResolver
			
情况描述 前端输入框输入中文的横线 -- ,到后台接收时变成了 &madsh;$mdash 正常应该显示成这样: bug调试思路记录 最开始完全没有向调试源码方面想,试了不少方法,都没解决,没 ...
 - Win10下数据增强及标注工具安装
			
Win10下数据增强及标注工具安装 一. 数据增强利器—Augmentor 1.安装 只需在控制台输入:pip install Augmentor 2.简介 Augmentor是用于图像增强的软件 ...
 - DataFoundation比赛总结
			
2018.3.20号左右,因为研究生的数据挖掘课程的老师要求我们集体参加一个比赛 ,所以在比赛参与时间.比赛难度和比赛类型的几种条件下,我们选择了2018平安产险数据建模大赛-驾驶行为预测驾驶风险比赛 ...
 - SVPWM总结
			
空间矢量算法 是以逆变器和电机作为一个整体来研究的.目标是产生电机定子的圆形磁场 模态选择, 上管导通 状态为1 下管导通 状态为0 那么状态为000 001 010 011 100 101 110 ...
 - [百度之星]资格赛:IP聚合
			
保持着也不知道什么情怀,觉得到现在才能发出来.这道题做完之后看了其他人的代码,然后再看我的,不得不说,真是幼稚的很,尤其是输入这一块,都什么跟什么啊. 但相较于之前来说,不像以前慌张了,学会先思考再去 ...
 - 动手实验01-----vCenter 微软AD认证配置与用户授权
			
环境说明: AD域-> centaline.net 阅读目录: 1. 配置与AD认证源 2.权限角色 1. 配置与AD认证源 登陆vCenter后,在 系统管理 -> 配置 -> ...
 - 解除TCL电视系统禁止adb安装
			
我的问题:tcl电视能使用adb连接成功,但使用adb install安装软件时,提示错误 解决问题如下: $adb shell,输入:1. $setprop persist.tcl.debug.in ...
 - 1-Java类结构和main函数
			
目录 Java类 main函数 1.Java类 - 类是java中最基础的逻辑单位 java中所有的内容都要放在类的范围中 - 类的构成 成员变量/属性 成员方法/函数 - java文件必须以.jav ...
 - .net core项目iis10上出现 HTTP 错误 500.19,错误代码:0x8007000d
			
文件权限更改,配置文件更改,IIS重装重启,各种办法都不管用,下面是解决办法: 看错误信息里:模块:IIS Web Core 打开IIS: 模块列表中是否缺少红框里的两个组件(我这是已经安装好的截图) ...