热门标签 | 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()

运行结果过长不予展示



推荐阅读
  • Python多线程编程技巧与实战应用详解 ... [详细]
  • 浅析python实现布隆过滤器及Redis中的缓存穿透原理_python
    本文带你了解了位图的实现,布隆过滤器的原理及Python中的使用,以及布隆过滤器如何应对Redis中的缓存穿透,相信你对布隆过滤 ... [详细]
  • 本文介绍如何使用OpenCV和线性支持向量机(SVM)模型来开发一个简单的人脸识别系统,特别关注在只有一个用户数据集时的处理方法。 ... [详细]
  • Ihavetwomethodsofgeneratingmdistinctrandomnumbersintherange[0..n-1]我有两种方法在范围[0.n-1]中生 ... [详细]
  • 多线程基础概览
    本文探讨了多线程的起源及其在现代编程中的重要性。线程的引入是为了增强进程的稳定性,确保一个进程的崩溃不会影响其他进程。而进程的存在则是为了保障操作系统的稳定运行,防止单一应用程序的错误导致整个系统的崩溃。线程作为进程的逻辑单元,多个线程共享同一CPU,需要合理调度以避免资源竞争。 ... [详细]
  • Python 序列图分割与可视化编程入门教程
    本文介绍了如何使用 Python 进行序列图的快速分割与可视化。通过一个实际案例,详细展示了从需求分析到代码实现的全过程。具体包括如何读取序列图数据、应用分割算法以及利用可视化库生成直观的图表,帮助非编程背景的用户也能轻松上手。 ... [详细]
  • QT框架中事件循环机制及事件分发类详解
    在QT框架中,QCoreApplication类作为事件循环的核心组件,为应用程序提供了基础的事件处理机制。该类继承自QObject,负责管理和调度各种事件,确保程序能够响应用户操作和其他系统事件。通过事件循环,QCoreApplication实现了高效的事件分发和处理,使得应用程序能够保持流畅的运行状态。此外,QCoreApplication还提供了多种方法和信号槽机制,方便开发者进行事件的定制和扩展。 ... [详细]
  • 探讨异步 Rust 中多线程代码无法实现并行化的原因及解决方案。 ... [详细]
  • Java高并发与多线程(二):线程的实现方式详解
    本文将深入探讨Java中线程的三种主要实现方式,包括继承Thread类、实现Runnable接口和实现Callable接口,并分析它们之间的异同及其应用场景。 ... [详细]
  • 解决Bootstrap DataTable Ajax请求重复问题
    在最近的一个项目中,我们使用了JQuery DataTable进行数据展示,虽然使用起来非常方便,但在测试过程中发现了一个问题:当查询条件改变时,有时查询结果的数据不正确。通过FireBug调试发现,点击搜索按钮时,会发送两次Ajax请求,一次是原条件的请求,一次是新条件的请求。 ... [详细]
  • 在 Java 中,`join()` 方法用于使当前线程暂停,直到指定的线程执行完毕后再继续执行。此外,`join(long millis)` 方法允许当前线程在指定的毫秒数后继续执行。 ... [详细]
  • 本文介绍了如何利用 `matplotlib` 库中的 `FuncAnimation` 类将 Python 中的动态图像保存为视频文件。通过详细解释 `FuncAnimation` 类的参数和方法,文章提供了多种实用技巧,帮助用户高效地生成高质量的动态图像视频。此外,还探讨了不同视频编码器的选择及其对输出文件质量的影响,为读者提供了全面的技术指导。 ... [详细]
  • 大类|电阻器_使用Requests、Etree、BeautifulSoup、Pandas和Path库进行数据抓取与处理 | 将指定区域内容保存为HTML和Excel格式
    大类|电阻器_使用Requests、Etree、BeautifulSoup、Pandas和Path库进行数据抓取与处理 | 将指定区域内容保存为HTML和Excel格式 ... [详细]
  • Python 程序转换为 EXE 文件:详细解析 .py 脚本打包成独立可执行文件的方法与技巧
    在开发了几个简单的爬虫 Python 程序后,我决定将其封装成独立的可执行文件以便于分发和使用。为了实现这一目标,首先需要解决的是如何将 Python 脚本转换为 EXE 文件。在这个过程中,我选择了 Qt 作为 GUI 框架,因为之前对此并不熟悉,希望通过这个项目进一步学习和掌握 Qt 的基本用法。本文将详细介绍从 .py 脚本到 EXE 文件的整个过程,包括所需工具、具体步骤以及常见问题的解决方案。 ... [详细]
  • 本文介绍了如何利用 Delphi 中的 IdTCPServer 和 IdTCPClient 控件实现高效的文件传输。这些控件在默认情况下采用阻塞模式,并且服务器端已经集成了多线程处理,能够支持任意大小的文件传输,无需担心数据包大小的限制。与传统的 ClientSocket 相比,Indy 控件提供了更为简洁和可靠的解决方案,特别适用于开发高性能的网络文件传输应用程序。 ... [详细]
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社区 版权所有