我的编程空间,编程开发者的网络收藏夹
学习永远不晚

线程、进程、协程和GIL(三)

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

线程、进程、协程和GIL(三)

上一篇文章介绍了:创建线程的两种方式、Event对象判断线程是否启动、利用信号量控制线程并发。

博客链接:线程、进程、协程和GIL(二)

这一篇来说说线程间通信的那些事儿: 

  一个线程向另一个线程发送数据最安全的方式就是使用queue库中的队列了,通过创建一个供多个线程共享的Queue对象,这些线程使用put()和get()操作来向队列中添加数据或者从队列中取出数据,以达到线程间通信的效果。

  queue队列基本方法:

    queue.Queue(maxsize = num):  FIFO  先进先出队列,如果maxsize小于或等于0 则代表队列长度无线。

    queue.LifoQueue(maxsize = num): LIFO 后进先出队列(类似于栈),如果maxsize小于或等于0 则代表队列长度无线。

    Queue.qsize(): 返回当前队列中元素的个数

         Queue.empty()   如果队列为空,返回True,反之False 

         Queue.full()   如果队列满了,返回True,反之False

         Queue.get([block[, timeout]])   读队列,timeout等待时间 

         Queue.put(item, [block[, timeout]])   写队列,timeout等待时间 

         Queue.queue.clear()   清空队列

  使用Queue构造生产者消费者模型来实现线程间的通信:

import time
from queue import Queue,LifoQueue
from threading import Thread

def producer(in_q):
    while True:
        time.sleep(1)
        data = '包子'
        if in_q.full() == True:
            print('蒸笼满了,放不下了')
        in_q.put(data)  # 向队列中塞东西
        print('小明蒸%s!' %(data))

def customer(out_q):
    while True:
        time.sleep(3)
        if out_q.empty() == True:
                print('小红没取到包子,饿死了!')
        data = out_q.get() # 从队列中取东西
        print('小红取 %s ' % (data))

if __name__ == '__main__':
    q = Queue(maxsize=3)
    t1 = Thread(target=producer, args=(q,))
    t2 = Thread(target=customer, args=(q,))
    t1.start()
    t2.start()

  上面的代码实现了一个简单的生产者消费者,小明负责蒸包子,小红负责吃包子。当队列被包子塞满时,小明就再也放不进去了,此时生产者这个线程就会阻塞。当小红将队列中的包子吃完时,消费者这个线程就会阻塞。因为Queue对象已经封装了必要的锁,所以我们可以在多个线程之间安全的功能共享数据。但是在生产者消费者的关闭问题会有一些麻烦,通用的解决方式就是在队列中放置一个特殊值,当消费者读到这个值时,就终止执行。

  不过有个问题需要注意:向队列中添加数据项时,并不会复制此数据项,线程间的通信实际上是在线程间传递对象引用。如果你单线对象的共享状态,那么最好只传递不可修改的数据结构(如:整型、字符串、或者元组)或者一个对象的深拷贝。

  给关键部分加锁

  线程的不安全:同一进程里线程是共享数据的,当各个线程访问同一个数据资源时会出现竞争状态,即数据可能会同时被多个线程占用,造成数据混乱,这就是线程的不安全。

   为了保证线程安全,所以引进了互斥锁,确保某段关键代码、共享数据只能由一个线程从头到尾完整地执行:

  显式的加锁:

from threading import Thread, Lock

num = 0
lock = Lock()  # 定义一个锁

def run():
    global num, lock  # 获取全局变量
    lock.acquire()  # 加锁
    num += 1
    print(num)
    lock.release()  # 释放锁

if __name__ == '__main__':
    Thread_list = []
    for i in range(1000):
        t = Thread(target=run)
        Thread_list.append(t)
    for i in Thread_list:
        i.start()

  死锁:但是加入互斥锁之后有可能会产生一个问题:死锁:若干子线程在系统资源竞争时,都在等待对方对某部分资源解除占用状态,结果谁也不愿意先解锁,互相等着,程序无法执行下去,这就是死锁。

  比如:有两个线程一、二,两个共享资源A、B,线程一给资源A加锁,线程二给资源B加锁,然后资源A需要访问资源B,资源B需要调用资源A,线程一二双方都在等待对方释放锁,所以就会造成死锁。

   But、当程序员在加锁之后忘记调用release()方法,或者加锁之后程序抛异常导致不能正常释放锁,有可能会造成死锁,为了避免这种情况,我们不需要显式的手动加锁和释放锁,而是使用with语句来进行自动控制:

  

from threading import Thread, Lock

num = 0
lock = Lock()  # 定义一个锁

def run():
    global num, lock
    with lock:  # 自动的控制加锁和释放锁
        num += 1
        print(num)

if __name__ == '__main__':
    Thread_list = []
    for i in range(1000):
        t = Thread(target=run)
        Thread_list.append(t)
    for i in Thread_list:
        i.start()

  创建一个线程池: 

  concurrent.futures 函数库有一个 ThreadPoolExecutor 类可以被用来完成这个任务

from concurrent.futures import ThreadPoolExecutor

def run():
    print('我是子线程')

if __name__ == '__main__':
    pool = ThreadPoolExecutor(max_workers=3)  # 创建一个容量为3的线程池
    for i in range(3):
        t = pool.submit(run,)  #在线程池中生成三个线程,他们都来调用run方法
    print('我是主线程')

 

想了解更多Python关于爬虫、数据分析的内容,欢迎大家关注我的微信公众号:悟道Python

  

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

线程、进程、协程和GIL(三)

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

线程、进程、协程和GIL(三)

上一篇文章介绍了:创建线程的两种方式、Event对象判断线程是否启动、利用信号量控制线程并发。博客链接:线程、进程、协程和GIL(二)这一篇来说说线程间通信的那些事儿:   一个线程向另一个线程发送数据最安全的方式就是使用queue库中的队
2023-01-30

python线程、进程和协程详解

引言解释器环境:python3.5.1我们都知道python网络编程的两大必学模块socket和socketserver,其中的socketserver是一个支持IO多路复用和多线程、多进程的模块。一般我们在socketserver服务端代
2022-06-04

Python进程/线程/协程

第1章 操作系统历史1.1为什么要有操作系统?程序员无法把所有的硬件操作细节全部了解到,管理这些硬件并且加以优化使用时非常繁琐的工作,这个繁琐的工作就是由操作系统来干的,有了它,程序员就从这些繁琐的工作中解脱了出来,只需要考虑自己的应用软件
2023-01-31

Python进程与线程及GIL(全局解释

MarkdownPad Document程序并不能单独运行,只有将程序装载到内存中,系统为它分配资源才能运行,而这种一个程序在一个数据集上的一次动态执行过程就称之为进程。程序和进程的区别就在于:程序是指令的集合,它是进程运行的静态描述文本;
2023-01-31

Python:线程、进程与协程(1)——

最近的业余时间主要放在了学习Python线程、进程和协程里,第一次用python的多线程和多进程是在两个月前,当时只是简单的看了几篇博文然后就跟着用,没有仔细去研究,第一次用的感觉它们其实挺简单的,最近这段时间通过看书, 看Python 中
2023-01-31

Python:线程、进程与协程(2)—

上一篇博文介绍了Python中线程、进程与协程的基本概念,通过这几天的学习总结,下面来讲讲Python的threading模块。首先来看看threading模块有哪些方法和类吧。主要有:Thread :线程类,这是用的最多的一个类,可以指定
2023-01-31

Python进程/线程/协程相关

1、获取进程ID。(getpid)os.getpid()2、获取父进程ID。(getppid)os.getppid()3、获取线程ID。(get_ident)(1)、进程内局部标识。import threadingthreading.get
2023-01-31

Python:线程、进程与协程(6)——

上篇博文介绍了multiprocessing模块的内存共享(点击此处可以参看),下面讲进程池。有些情况下,所要完成的工作可以上篇博文介绍了multiprocessing模块的内存共享,下面讲进程池。有些情况下,所要完成的工作可以分解并独立地
2023-01-31

Python的进程,线程和协程实例分析

这篇“Python的进程,线程和协程实例分析”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“Python的进程,线程和协程实例
2023-06-29

Python:线程、进程与协程(3)——

Queue模块是提供队列操作的模块,队列是线程间最常用的交换数据的形式。该模块提供了三种队列:Queue.Queue(maxsize):先进先出,maxsize是队列的大小,其值为非正数时为无线循环队列Queue.LifoQueue(max
2023-01-31

Python:线程、进程与协程(7)——

前面转载了一篇分析进程池源码的博文,是一篇分析进程池很全面的文章,点击此处可以阅读。在Python中还有一个线程池的概念,它也有并发处理能力,在一定程度上能提高系统运行效率;不正之处欢迎批评指正。     线程的生命周期可以分为5个状态:创
2023-01-31

多任务中进程、线程、协程

为什么使用多任务?有什么好处?答:提高程序执行效率,实现同一时刻可以做二个以上的事情。啦啦啦。。。重点来了并行:任务数大于CPU核数,每个CPU就要执行多个任务,那肯定忙不过来,多个任务执行就需要排队等待上一任务执行完,才能执行下一任务。并
2023-01-30

swoole协程是进程还是线程

今天小编给大家分享一下swoole协程是进程还是线程的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。swoole协程是属于线程
2023-06-29

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录