热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

python线程卡死问题解决_Python多线程,线程死锁及解决,生产者与消费者问题

1.Thread类普通调用tThread(targettest,args(i,))#test为目标函数名,若函数需要参数将其以元组形#式赋给args,若无参数可不写t.sta

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 &#43;&#61; 1

msg &#61; &#39;生成产品&#39; &#43; 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 &#61; self.name &#43; &#39;消费了&#39; &#43; queue.get()

print(msg)

time.sleep(1)

if __name__ &#61;&#61; &#39;__main__&#39;:

queue &#61; Queue()

for i in range(500):

queue.put(&#39;初始产品&#39;&#43;str(i)) # 向队列中塞内容

for i in range(2):

p &#61; Producer()

p.start()

for i in range(5):

c &#61; Consumer()

c.start()

运行结果过长不予展示



推荐阅读
  • 本文由编程笔记#小编为大家整理,主要介绍了logistic回归(线性和非线性)相关的知识,包括线性logistic回归的代码和数据集的分布情况。希望对你有一定的参考价值。 ... [详细]
  • 本文介绍了Python对Excel文件的读取方法,包括模块的安装和使用。通过安装xlrd、xlwt、xlutils、pyExcelerator等模块,可以实现对Excel文件的读取和处理。具体的读取方法包括打开excel文件、抓取所有sheet的名称、定位到指定的表单等。本文提供了两种定位表单的方式,并给出了相应的代码示例。 ... [详细]
  • Python瓦片图下载、合并、绘图、标记的代码示例
    本文提供了Python瓦片图下载、合并、绘图、标记的代码示例,包括下载代码、多线程下载、图像处理等功能。通过参考geoserver,使用PIL、cv2、numpy、gdal、osr等库实现了瓦片图的下载、合并、绘图和标记功能。代码示例详细介绍了各个功能的实现方法,供读者参考使用。 ... [详细]
  • 本文介绍了机器学习手册中关于日期和时区操作的重要性以及其在实际应用中的作用。文章以一个故事为背景,描述了学童们面对老先生的教导时的反应,以及上官如在这个过程中的表现。同时,文章也提到了顾慎为对上官如的恨意以及他们之间的矛盾源于早年的结局。最后,文章强调了日期和时区操作在机器学习中的重要性,并指出了其在实际应用中的作用和意义。 ... [详细]
  • 本文介绍了Python爬虫技术基础篇面向对象高级编程(中)中的多重继承概念。通过继承,子类可以扩展父类的功能。文章以动物类层次的设计为例,讨论了按照不同分类方式设计类层次的复杂性和多重继承的优势。最后给出了哺乳动物和鸟类的设计示例,以及能跑、能飞、宠物类和非宠物类的增加对类数量的影响。 ... [详细]
  • 深入理解Kafka服务端请求队列中请求的处理
    本文深入分析了Kafka服务端请求队列中请求的处理过程,详细介绍了请求的封装和放入请求队列的过程,以及处理请求的线程池的创建和容量设置。通过场景分析、图示说明和源码分析,帮助读者更好地理解Kafka服务端的工作原理。 ... [详细]
  • 第四章高阶函数(参数传递、高阶函数、lambda表达式)(python进阶)的讲解和应用
    本文主要讲解了第四章高阶函数(参数传递、高阶函数、lambda表达式)的相关知识,包括函数参数传递机制和赋值机制、引用传递的概念和应用、默认参数的定义和使用等内容。同时介绍了高阶函数和lambda表达式的概念,并给出了一些实例代码进行演示。对于想要进一步提升python编程能力的读者来说,本文将是一个不错的学习资料。 ... [详细]
  • 基于dlib的人脸68特征点提取(眨眼张嘴检测)python版本
    文章目录引言开发环境和库流程设计张嘴和闭眼的检测引言(1)利用Dlib官方训练好的模型“shape_predictor_68_face_landmarks.dat”进行68个点标定 ... [详细]
  • 十大经典排序算法动图演示+Python实现
    本文介绍了十大经典排序算法的原理、演示和Python实现。排序算法分为内部排序和外部排序,常见的内部排序算法有插入排序、希尔排序、选择排序、冒泡排序、归并排序、快速排序、堆排序、基数排序等。文章还解释了时间复杂度和稳定性的概念,并提供了相关的名词解释。 ... [详细]
  • Python使用Pillow包生成验证码图片的方法
    本文介绍了使用Python中的Pillow包生成验证码图片的方法。通过随机生成数字和符号,并添加干扰象素,生成一幅验证码图片。需要配置好Python环境,并安装Pillow库。代码实现包括导入Pillow包和随机模块,定义随机生成字母、数字和字体颜色的函数。 ... [详细]
  • 云原生边缘计算之KubeEdge简介及功能特点
    本文介绍了云原生边缘计算中的KubeEdge系统,该系统是一个开源系统,用于将容器化应用程序编排功能扩展到Edge的主机。它基于Kubernetes构建,并为网络应用程序提供基础架构支持。同时,KubeEdge具有离线模式、基于Kubernetes的节点、群集、应用程序和设备管理、资源优化等特点。此外,KubeEdge还支持跨平台工作,在私有、公共和混合云中都可以运行。同时,KubeEdge还提供数据管理和数据分析管道引擎的支持。最后,本文还介绍了KubeEdge系统生成证书的方法。 ... [详细]
  • 本文讨论了如何优化解决hdu 1003 java题目的动态规划方法,通过分析加法规则和最大和的性质,提出了一种优化的思路。具体方法是,当从1加到n为负时,即sum(1,n)sum(n,s),可以继续加法计算。同时,还考虑了两种特殊情况:都是负数的情况和有0的情况。最后,通过使用Scanner类来获取输入数据。 ... [详细]
  • 也就是|小窗_卷积的特征提取与参数计算
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了卷积的特征提取与参数计算相关的知识,希望对你有一定的参考价值。Dense和Conv2D根本区别在于,Den ... [详细]
  • [大整数乘法] java代码实现
    本文介绍了使用java代码实现大整数乘法的过程,同时也涉及到大整数加法和大整数减法的计算方法。通过分治算法来提高计算效率,并对算法的时间复杂度进行了研究。详细代码实现请参考文章链接。 ... [详细]
  • SpringMVC接收请求参数的方式总结
    本文总结了在SpringMVC开发中处理控制器参数的各种方式,包括处理使用@RequestParam注解的参数、MultipartFile类型参数和Simple类型参数的RequestParamMethodArgumentResolver,处理@RequestBody注解的参数的RequestResponseBodyMethodProcessor,以及PathVariableMapMethodArgumentResol等子类。 ... [详细]
author-avatar
拍友2502868875
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有