线程间通信 | 学习笔记

简介: 快速学习 线程间通信

开发者学堂课程【Python入门 2020年版线程间通信】学习笔记,与课程紧密联系,让用户快速学习知识。

课程地址:https://developer.aliyun.com/learning/course/639/detail/10469


线程间通信


内容介绍

一、线程间通信

二、举例说明


一、线程间通信

1.简单介绍

线程之间有时需要通信,操作系统提供了很多机制来实现进程间的通信,其中我们使用最多的是队列 Queue

2.Queue 的原理

Queue 是一个先进先出 (First in First Out)的队列,主进程中创建一个 Queue 对象,并作为参数传入子进程,两者之间通过 put( )放入数据,通过 get( )取出数据,执行了 get( )涵数之后队列中的数据会被同时删除,可以使用multiprocessing 模块的 Queue 实现多进程之间的数据传递。

import threading

import time

from gqutue import Queue

def producer (queue):

for i in range(100) :

print('{}存入了

{}" .format(threading.current_thread().name,i))

queue.put(i)

time .sleep(0.1)

Return

def consumer(queue):


二、举例说明

最常见的例子:

生产和消费者,去面包店买面包,一个是生产面包的(生产者),一个是买面包的(消费者),他们要同时操作一个数据。有两个线程一个生产者,一个消费者,他们都来访问面包,生产者增加面包,消费者来取面包,面包减少。

两个线程如何通信,用全局变量会出现线程安全问题。Queue 结构可以在不同的线程间传递数据。

有一个问题消费者一直在消费,面包卖完了,消费者买不到,需要等待。面包生产一个就能买到一个,生产快,面包就要堆积,生产慢,消费者就要等待。

1.def produce():

for i in range (10):

print(‘生产了面包’)

def consumer():

for i in range(10):

print(‘买到了面包’)

p1= threading.Thread(target=producename=p1)

(注意:produce 后面不能加括号,加括号是函数调用)

c1=threading.Thread(target=consumer,name=’c1)

p1.start()

c1.start()

我们的目标是让生产者线程里生产的面包能够让消费者线程拿到。

2.import threadingqueue   #导入queue

def produce():

for i in range (10):

time.sleep(1)

print(‘生产+++++面包b{}.format(i))

q.put(‘b{}.format(i))

def consumer():

for i in range(100):

time.sleep(0.3)

#q.get()方法是一个阻塞的方法

print(‘买到-----面包{}.format(q.get)))

q=queue.Queue  #创建全局变量 q

p1= threading.Thread(target=producename=p1)

c1=threading.Thread(target=consumer,name=’c1)

p1.start()

c1.start()

生产一个面包就放一个进队列 b0b1b2...b10,为了看得更明显,我们加上 time.sleep

运行时发现,有一段时间什么都没打印,因为 q.get 的方法是一个阻塞的方法,一上来的时候都要歇,1秒要停,0.3秒要停,肯定先走0.3秒的,但是对列里没有东西可以拿,所以它在等待,会有1秒什么都没打印。

3.对列结构

对列结构很有意思,生产 b0cpu 此时切换到消费者直接拿走了,紧接着生产了 b1b2,先拿 b1,又生产 b3b4,消费者又拿了 b2。这叫“先进先出”。

Stack 栈结构是“后进先出,先进后出”。

4.复杂场景

import threadingqueue   #导入queue

def produce():

for i in range (10):

time.sleep(1)

print(‘生产+++++面包{}

{}.format(threading.current_thread().name,i))

q.put(‘{} {}.format((threading.current_thread().name,i))

def consumer():

for i in range(100):

time.sleep(0.3)

#q.get()方法是一个阻塞的方法

print({}买到-----面包

{}’.format(threading.current_thread().name,q.get)))

q=queue.Queue #创建全局变量 q

#一条生产线

pa= threading.Thread(target=producename=p1)

#一条消费线

ca=threading.Thread(target=consumer,name=’c1)

pa.start()

ca.start()

运行打印显示

生产+++++面包 pa 0

生产+++++面包 pa 1

ca 买到-----面包 pa 0

生产+++++面包 pa 2

生产+++++面包 pa 3

生产+++++面包 p1 4

ca 买到-----面包 pa 2

生产+++++面包 p1 5

生产+++++面包 p1 6

ca 买到-----面包 pa 3

......

如果多两个生产线,就会严重地供过于求。

import threadingqueue   #导入queue

def produce():

for i in range (10):

time.sleep(1)

print(‘生产+++++面包{}

{}.format(threading.current_thread().name,i))

q.put(‘{} {}.format((threading.current_thread().name,i))

def consumer():

for i in range(100):

time.sleep(0.3)

#q.get()方法是一个阻塞的方法

print({}买到-----面包

{}’.format(threading.current_thread().name,q.get)))

q=queue.Queue#创建全局变量 q

#一条生产线

pa= threading.Thread(target=producename=p1)

pb= threading.Thread(target=producename=p1)

pc= threading.Thread(target=produce,name=p1)

#一条消费线

ca=threading.Thread(target=consumer,name=’c1)

pa.start()

pb.start()

pc.start()

ca.start()

运行打印显示

生产+++++面包 pb 0

生产+++++面包 pa 0

生产+++++面包 pc 0

生产+++++面包 pb 1

ca 买到-----面包 pb1

生产+++++面包 pa 1

生产+++++面包 pc 1

生产+++++面包 pb 2

生产+++++面包 pa 2

生产+++++面包 pc 2

ca 买到-----面包 pa 0

......

最后的面包没有买完,因为我每个线程生产了10个,只开了一个线程买10次,所以卖不完,直接消费者写个死循环,就可以买完。死循环不用停止,就是等待任务。

def consumer():

while True:

time.sleep(0.3)

#q.get()方法是一个阻塞的方法

print({}买到-----面包

{}’.format(threading.current_thread().name,q.get)))

再加两条生产线

ca=threading.Thread(target=consumer,name=c1)

cb=threading.Thread(target=consumer,name=c1)

cc=threading.Thread(target=consumer,name=’c1)

手机怎么知道你来电话呢,因为后台运行一个死循环一直找对列里有没有任务,有个电话来了,就把消息发到q对列来,通知你电话来了,来一个消息就处理一个。这里不要break,不要停掉。手机里有很多死循环。

例如服务器里的应用,当你注册完了以后,留一个邮箱,会有个点击按钮发送邮件,发邮件这属于是比较耗时的网络操作,如果在主线程里发,发送等待的5-10s 基本是卡死的状态,可以把任务交给子线程,后面会讲。

注意:不能在主线程里死循环。

相关文章
|
1月前
|
编解码 数据安全/隐私保护 计算机视觉
Opencv学习笔记(十):同步和异步(多线程)操作打开海康摄像头
如何使用OpenCV进行同步和异步操作来打开海康摄像头,并提供了相关的代码示例。
88 1
Opencv学习笔记(十):同步和异步(多线程)操作打开海康摄像头
|
26天前
|
Java 调度
[Java]线程生命周期与线程通信
本文详细探讨了线程生命周期与线程通信。文章首先分析了线程的五个基本状态及其转换过程,结合JDK1.8版本的特点进行了深入讲解。接着,通过多个实例介绍了线程通信的几种实现方式,包括使用`volatile`关键字、`Object`类的`wait()`和`notify()`方法、`CountDownLatch`、`ReentrantLock`结合`Condition`以及`LockSupport`等工具。全文旨在帮助读者理解线程管理的核心概念和技术细节。
38 1
[Java]线程生命周期与线程通信
|
12天前
|
Java
JAVA多线程通信:为何wait()与notify()如此重要?
在Java多线程编程中,`wait()` 和 `notify()/notifyAll()` 方法是实现线程间通信的核心机制。它们通过基于锁的方式,使线程在条件不满足时进入休眠状态,并在条件满足时被唤醒,从而确保数据一致性和同步。相比其他通信方式,如忙等待,这些方法更高效灵活。 示例代码展示了如何在生产者-消费者模型中使用这些方法实现线程间的协调和同步。
27 3
|
4月前
|
Java
实现Java多线程中的线程间通信
实现Java多线程中的线程间通信
|
27天前
|
安全 Java
Java多线程通信新解:本文通过生产者-消费者模型案例,深入解析wait()、notify()、notifyAll()方法的实用技巧
【10月更文挑战第20天】Java多线程通信新解:本文通过生产者-消费者模型案例,深入解析wait()、notify()、notifyAll()方法的实用技巧,包括避免在循环外调用wait()、优先使用notifyAll()、确保线程安全及处理InterruptedException等,帮助读者更好地掌握这些方法的应用。
17 1
|
27天前
|
安全 Java 开发者
Java多线程中的`wait()`、`notify()`和`notifyAll()`方法,探讨了它们在实现线程间通信和同步中的关键作用
本文深入解析了Java多线程中的`wait()`、`notify()`和`notifyAll()`方法,探讨了它们在实现线程间通信和同步中的关键作用。通过示例代码展示了如何正确使用这些方法,并分享了最佳实践,帮助开发者避免常见陷阱,提高多线程程序的稳定性和效率。
34 1
|
27天前
|
Java
在Java多线程编程中,`wait()` 和 `notify()/notifyAll()` 方法是线程间通信的核心机制。
在Java多线程编程中,`wait()` 和 `notify()/notifyAll()` 方法是线程间通信的核心机制。它们通过基于锁的方式,使线程在条件不满足时进入休眠状态,并在条件成立时被唤醒,从而有效解决数据一致性和同步问题。本文通过对比其他通信机制,展示了 `wait()` 和 `notify()` 的优势,并通过生产者-消费者模型的示例代码,详细说明了其使用方法和重要性。
25 1
|
1月前
|
Java
|
1月前
多线程通信和同步的方式有哪些?
【10月更文挑战第6天】
100 0
|
1月前
FFmpeg学习笔记(二):多线程rtsp推流和ffplay拉流操作,并储存为多路avi格式的视频
这篇博客主要介绍了如何使用FFmpeg进行多线程RTSP推流和ffplay拉流操作,以及如何将视频流保存为多路AVI格式的视频文件。
174 0