Python中线程间通信

  • 一、前言
  • 二、什么是互斥锁
  • 三、使用互斥锁
  • 四、使用队列在线程间通信
  • 五、关于线程需要注意的两点

一、前言

  我们已经知道进程之间不能直接共享信息,那么线程之间可以共享信息吗?我们通过一个例子来验证一下。定义一个全局变量g_num,分别创建2个子进程对g_num执行不同的操作,并输出操作后的结果。代码如下:

# _*_ coding:utf-8 _*_
from threading import Thread  # 导入线程
import timedef plus():print("-------子线程1开始---------")global g_num  # 定义全局变量g_num += 50  # 全局变量值加50print("g_num is %d" % g_num)print("-------子线程1结束---------")def minus():time.sleep(2)print("-------子线程2开始---------")global g_num  # 定义全局变量g_num -= 50  # 全局变量值减50print("g_num is %d" % g_num)print("-------子线程2结束---------")g_num = 100  # 定义一个全局变量if __name__ == "__main__":print("----------主线程开始-------------")print("g_num is %d" % g_num)t1 = Thread(target=plus)  # 实例化线程t1t2 = Thread(target=minus)  # 实例化线程t2t1.start()  # 开启线程t1t2.start()  # 开启线程t2t1.join()  # 等待t1线程结束t2.join()  # 等待t2线程结束print("--------主线程结束-------------")

  上述代码中,定义一个全局变量g_num,赋值为100,然后创建2个线程。一个线程将g_num增加50,一个线程将g_num减少50。如果g_num的最终结果为100,则说明线程之间可以共享数据,运行结果如下图所示:

  从上面的例子可以得出,在一个进程内的所有线程共享全局变量,能够在不使用其他方式的前提下完成多线程之间的数据共享。


二、什么是互斥锁

  由于线程可以对全局变量随意修改,这就可能造成多线程之间对线程的混乱操作。以房子为例,当房子内只有一个居住者时(单线程),他可以任意时刻使用任意一个房间,如厨房、卧室和卫生间等。但是,当这个房子有多个居住者时(多线程),他就不能在任意时刻使用某些房间,如卫生间,否则会造成混乱。

  如何解决这个问题呢?一个防止他人进入的简单方法,就是门上加一把锁。就是先到的人在门口排队,等锁打开再进去。如图所示:

  这就是“互斥锁”(Mutual exclude,缩写Mutex),防止多个线程同时读写某一块内存区域。互斥锁为资源引入一个状态:锁定和非锁定。某个线程要更改共享数据时,先将其锁定,此时资源的状态为“锁定”,其他线程不能更改;直到该线程释放资源,将资源的状态变成“非锁定”时,其他的线程才能再次锁定该资源。互斥锁保证了每次只有一个线程进行写入操作,从而保证了多线程情况下数据的准确性。


三、使用互斥锁

  在threading模块中使用Lock类可以方便地处理锁定。Lock类有两个方法:acquire()锁定和release()释放锁。实例用法如下:

mutex = threading.Lock()    # 创建锁
mutex.acquire([blocking])    # 锁定
mutex.release()                   # 释放锁

  语法如下:

   acquire([blocking]) :获取锁定,如果有必要,需要阻塞到锁定释放为止。如果提供blocking参数并将设置为False,当无法获取锁定时将立即返回False;如果成功获取锁定则返回True。

  release():释放一个锁定,当锁定处于未锁定状态时,或者从与原本调用acquire()方法的不同线程调用此方法,将出现错误。

  下面,通过一个示例学习一下如何使用互斥锁。这里使用多线程和互斥锁模拟实现多人同时订购电影票的功能,假设电影院某个场次只有100张电影票,10个用户同时抢购该电影票。每出售一张,显示一次剩余的电影票张数。代码如下:

# _*_ coding:utf-8 _*_
from threading import Thread, Lock
import timen = 100  # 共100张票def task():global nmutex.acquire()  # 上锁temp = n  # 赋值给临时变量time.sleep(0.1)  # 休眠0.1秒n = temp - 1  # 数量减1print("购买成功,剩余%d张电影票" % n)mutex.release()  # 释放锁if __name__ == "__main__":mutex = Lock()  # 实例化Lock类t_l = []  # 初始化一个列表for i in range(10):t = Thread(target=task)  # 实例化线程类t_l.append(t)  # 将线程实例存入列表中t.start()  # 创建线程for t in t_l:t.join()  # 等待子线程结束

  上述代码中,创建了10个线程,全部执行task()函数。为了解决资源竞争问题,使用mutex.acquire() 函数实现资源锁定,第一个获取资源的线程锁定后,其他线程等待 mutex.release() 解锁,所以每次只有一个线程执行task()函数。运行结果如下图所示:


  注意:
  使用互斥锁时,要避免死锁。在多任务系统下,当一个或多个线程等待系统资源,而资源又被线程本身或其他线程占用时,就行程了死锁,如图所示:


四、使用队列在线程间通信

  我们知道multiprocessing 模块的Queue队列可以实现进程间通信,同样在线程间,也可以使用Queue队列实现线程间通信。不同之处在于我们需要使用queue 模块的Queue队列,而不是multiprocessing模块的Queue队列,但是Queue的使用方法相同。

  使用Queue在线程间通信通常应用于生产这消费模式。产生数据的模块称为生产者,而处理数据的模块称为消费者。在生产者与消费者之间的缓冲区称之为仓库。生产者负责往仓库运输商品,而消费者负责从仓库里取出商品,这就构成了生产消费模式。下面通过一个示例学习一下使用Queue在线程间的通信。

  定义一个生产者类Producer,定义一个消费者类Consumer。生产者生成5件产品,依次写入队列,而消费者依次从队列中取出产品,代码如下:

# _*_ coding:utf-8 _*_from queue import Queue
import random, threading, time# 生产者类
class Producer(threading.Thread):def __init__(self, name, queue):threading.Thread.__init__(self, name=name)self.data = queuedef run(self):for i in range(5):print("生产者%s将产品%d加入队列!" % (self.getName(), i))self.data.put(i)time.sleep(random.random())print("生产者%s完成" % self.getName())# 消费者类
class Consumer(threading.Thread):def __init__(self, name, queue):threading.Thread.__init__(self, name=name)self.data = queuedef run(self):for i in range(5):val = self.data.get()print("消费者%s将产品%d从队列中取出!" % (self.getName(), val))time.sleep(random.random())print("消费者%s完成" % self.getName())if __name__ == "__main__":print("----------主线程开始------------")queue = Queue()  # 实例化队列producer = Producer("Producer", queue)  # 实例化线程Producer,并传入队列作为参数consumer = Consumer("Consumer", queue)  # 实例化线程Consumer,并传入队列作为参数producer.start()  # 启动线程Producerconsumer.start()  # 启动线程Consumerproducer.join()  # 等待线程Producer结束consumer.join()  # 等待线程Consumer结束print("----------主线程结束--------------")

  运行结果如下图所示:

  注意:
  由于程序中使用了random.random()函数生成0-1之间的随机数,所以大家运行结果可能与图不一样。


五、关于线程需要注意的两点

  【1】进程和线程的区别

  进程和线程的区别主要有:

  (1)进程是系统进行资源分配和调度的一个单位,线程是一个实体,是CPU调度和分派的基本单位。

  (2)进程之间相互独立的,多进程中,同一个变量,各自有一份备份存在于每个进程中,但互不影响;而同一个进程的多个线程是内存共享的,所有变量都由所有线程共享。

  (3)由于进程间是独立的,因此一个进程的奔溃不会影响到其他进程;而线程是包含在进程之内的,线程奔溃会引发进程的奔溃,继而导致同一进程内的其他进程也奔溃。


  【2】多线程非全局变量是否要加锁

  在多线程开发中,全局变量是多个线程都共享的数据,为了防止数据混乱,通常使用互斥锁。而局部变量等是各自线程的,是非共享的,所以不需要使用互斥锁。


Python中线程间通信相关推荐

  1. python线程间通信方法之Event

    最近写程序要用到线程间通信,在网上搜了些资料,测试了一下代码,在这里总结一下. Python实现线程间通信有几种方法,在这里介绍Event对象. Event对象实现了简单的线程通信机制,它提供了设置信 ...

  2. java 线程间通信 handler_Handler不同线程间的通信

    转http://www.iteye.com/problems/69457 Activity启动后点击一个界面按钮后会开启一个服务(暂定为padService),在padService中会启动一个线程( ...

  3. Python中的线程间通信

    Python中的线程间通信 文章目录 Python中的线程间通信 1.Queue 2.同步机制 1.Event 2.Semaphore(信号量) 3.Lock(锁) 4.RLock(可重入锁) 5.C ...

  4. python 线程通信 会涉及到拷贝吗_Python如何实现线程间通信

    问题 你的程序中有多个线程,你需要在这些线程之间安全地交换信息或数据 解决方案 从一个线程向另一个线程发送数据最安全的方式可能就是使用 queue 库中的队列了.创建一个被多个线程共享的 Queue ...

  5. python 线程通信的几种方式_进程间通信和线程间通信的几种方式

    进程 进程(Process)是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础.在早期面向进程设计的计算机结构中,进程是程序的基本执行实体:在当代 ...

  6. python线程间通信_python多线程之事件触发(线程间通信)

    执行结果: 那么,通过分析执行结果来看,您已经体会到了其中的秘密.... 再脑补一下: Python提供了Event对象用于线程间通信,它是由线程设置的信号标志,如果信号标志位真,则其他线程等待直到信 ...

  7. [Python]线程实例化;互斥锁;线程间通信

    目录 1.使用threading创建线程 2.线程锁(互斥锁) 线程间通信 ----------到此,结束------------ 图穷匕见 1.使用threading创建线程 threading模块 ...

  8. 线程间通信的几种方法_并发编程中的线程间通信

    线程通信的目标是使线程间能够互相发送信号.另一方面,线程通信使线程能够等待其他线程的信号. 线程通信常用的方式有: wait/notify 等待 Volatile 内存共享 CountDownLatc ...

  9. android线程间通信的几种方法_Android 技能图谱学习路线

    Java基础 Java Object类方法 HashMap原理,Hash冲突,并发集合,线程安全集合及实现原理 HashMap 和 HashTable 区别 HashCode 作用,如何重载hashC ...

最新文章

  1. 关于负载均衡的三种传输模式(反向代理,透传,三角)
  2. 密码学:RSA加密算法详解
  3. Linux学习笔记04
  4. React 在body上绑定事件以及阻止事件冒泡
  5. Spring-data-jpa中用@ColumnTransformer注解加密,中文乱码问题(数据库正常,在java代码和页面中乱码)
  6. php找不到库,64位系统下编译PHP找不到库文件问题 | 学步园
  7. 【Linux】完美解决 nginx 的权限问题( Permission denied)
  8. 扩展二叉树 (根据特殊的前序遍历建树)
  9. 电脑系统哪个好用_火绒杀毒,真有那么好用吗?
  10. RocketMQ之消费者并发消费源码解析
  11. GB2312和ASCII码点阵字库HZK, ASC整理
  12. 分享多年收集的40款免费开源源码
  13. 浅谈IPFS新激励层Filenet究竟是什么,令牌、公链、应用?超级云系统!
  14. wap2app是什么
  15. 什么是Harmony操作系统?华为新操作系统介绍
  16. c语言随机数 抛硬币,C语言 抛硬币的问题
  17. screenX、clientX、pageX三者间的区别
  18. [矩阵论] 谱半径小于1,则I-A可逆
  19. JavaScript深入浅出第2课:函数是一等公民是什么意思呢?
  20. 俄方产量也将削减恐左右油价未来走势

热门文章

  1. 基于快速搜索与寻找密度峰值的聚类方法
  2. w ndows只能安装到gpt磁盘,安装win10系统时提示windows只能安装到GPT磁盘如何解决[多图]...
  3. Android 开发--利用android studio 制作简单文字打怪升级游戏(伪地牢类)1.开始
  4. 叠瓦盘为什么不推荐_叠瓦盘还是固态盘还是垂直盘?
  5. 【数字化十大预测】德勤发布《2019科技、传媒和电信行业预测》
  6. [国产单片机] 聊聊曾经那些很火的单片机
  7. Python爬取Post请求
  8. BBC news reading 3--前缀 patr
  9. 『Python-Django 智慧中医健康数字服务平台』开源项目总览
  10. 最前沿的武器!磁力弹射器和磁力弹射枪 DIY 威力强大。视频!