当前位置: 首页 > 编程日记 > 正文

Linux编程之自定义消息队列

我这里要讲的并不是IPC中的消息队列,我要讲的是在进程内部实现自定义的消息队列,让各个线程的消息来推动整个进程的运动。进程间的消息队列用于进程与进程之间的通信,而我将要实现的进程内的消息队列是用于有序妥当处理来自于各个线程请求,避免一窝蜂的请求而导致消息的异常丢失。想想socket编程里的listen函数吧,里面要设置一个队列长度的参数,其实来自网络的请求已经排成一个请求队列了,只是这个队列是系统帮我们做好了,我们看不到而已。如果系统不帮我们做这个等待队列的话,那就需要我们程序员在应用层实现了。


进程内的消息队列实现并不难,总的来说有以下几点:
  • 自定义消息结构,并构造队列
  • 一个线程负责依次从消息队列中取出消息,并处理该消息
  • 多个线程产生事件,并将消息放进消息队列,等待处理
长话短说,我们开始动手吧!
一、定义消息结构
先贴代码再解释:
typedef struct Msg_Hdr_s  
{  uint32 msg_type;  uint32 msg_len;  uint32 msg_src;  uint32 msg_dst;      
}Msg_Hdr_t;  typedef struct Msg_s  
{  Msg_Hdr_t hdr;  uint8 data[100];  
} Msg_t;
下面是我设计的消息格式内容的解释:
  • msg_type:标记消息类型,当消息接收者看到该msg_type后就知道他要干什么事了
  • msg_len:消息长度,待扩展,暂时没用到(以后会扩展为变长消息)
  • msg_src:消息的源地址,即消息的发起者
  • msg_dst:消息的目的地,即消息的接受者
  • data[100]:消息除去消息头外可以携带的信息量,定义为100字节
由该消息数据结构可以知道,这个消息是定长的,当然也可以实现为变长消息,但现在暂不实现,今天先把定长消息实现了,以后再完善变长消息。
二、构造循环队列
队列可以由链表实现,也可以由数组实现,这里就使用数组实现的循环链表作为我们消息队列的队列模型。
typedef struct Queue_s  
{  int head;  int rear;  sem_t sem;  Msg_t data[QUEUE_SIZE];  
}Queue_t;  int MsgQueueInit(Queue_t* Q)  
{  if(!Q)  {  printf("Invalid Queue!\n"); return -1; } Q->rear = 0; Q->head = 0; sem_init(&Q->sem, 0, 1); return 0; } int MsgDeQueue(Queue_t* Q, Msg_t* msg) { if(!Q) { printf("Invalid Queue!\n"); return -1; } if(Q->rear == Q->head) //only one consumer,no need to lock head  { printf("Empty Queue!\n"); return -1; } memcpy(msg, &(Q->data[Q->head]), sizeof(Msg_t)); Q->head = (Q->head+1)%QUEUE_SIZE; return 0; } int MsgEnQueue(Queue_t* Q, Msg_t* msg) { if(Q->head == (Q->rear+1)%QUEUE_SIZE) { printf("Full Queue!\n"); return -1; } sem_wait(&Q->sem); memcpy(&(Q->data[Q->rear]), msg, sizeof(Msg_t)); Q->rear = (Q->rear+1)%QUEUE_SIZE; sem_post(&Q->sem); return 0; } 
循环队列的实现想必大家都比较熟悉,但这里需要提示的几点是:
  • 队列中应加入信号量或锁来保证进队时的互斥访问,因为多个消息可能同时进队,互相覆盖其队列节点
  • 这里的信号量仅用于进队而没用于出队,理由是消息处理者只有一个,不存在互斥的情形

三、构造消息处理者

if(pthread_create(&handler_thread_id, NULL, (void*)msg_handler, NULL))  
{  printf("create handler thread fail!\n");  return -1;          
}  void msg_printer(Msg_t* msg)  
{  if(!msg)  {  return; } printf("%s: I have recieved a message!\n", __FUNCTION__); printf("%s: msgtype:%d msg_src:%d dst:%d\n\n",__FUNCTION__,msg->hdr.msg_type,msg->hdr.msg_src,msg->hdr.msg_dst); } void msg_handler() { sleep(5); //let's wait 5s when starts while(1) { Msg_t msg; memset(&msg, 0 ,sizeof(Msg_t)); int res = MsgDeQueue((Queue_t*)&MsgQueue, &msg); if(res != 0) { sleep(10); continue; } msg_printer(&msg); sleep(1); } }
我在进程里create了一个线程作为消息处理者(handler)来处理消息队列的消息,甘进入该线程时先等个5秒钟来让生产者往队列里丢些消息,然后再开始消息处理。当队列没消息可取时,就休息十秒,再去取消息。
这里的消息处理很简单,我只是简单地将受到的消息打印一下,证明受到的消息正是其他线程发给我的。当然,你也可以在这里扩展功能,根据受到的消息类型进一步决定该做什么事。比如:
enum MSG_TYPE  
{  GO_HOME,  GO_TO_BED,  GO_TO_LUNCH,  GO_TO_CINAMA,  GO_TO_SCHOOL,  GO_DATEING,  GO_TO_WORK,//6  
};  void handler()  
{  switch(msgtype)  {  case GO_HOME: go_home(); break;  case GO_TO_BED:  go_to_bed(); break; ....... } }

这里的handler就是一个简单的状态机了,根据给定的消息类型(事件)去做特定的事,推动状态机的转动。

四、构造消息生产者

if(pthread_create(&thread1_id, NULL, (void*)msg_sender1, NULL))  
{  printf("create thread1 fail!\n");  return -1;  
}  if(pthread_create(&thread2_id, NULL, (void*)msg_sender2, NULL))  
{  printf("create thread2 fail!\n");  return -1; } if(pthread_create(&thread3_id, NULL, (void*)msg_sender3, NULL)) { printf("create thread3 fail!\n"); return -1; } void msg_sender1() { int i = 0; while(1) { if(i > 10) { i = 0; } Msg_t msg; msg.hdr.msg_type = i++; msg.hdr.msg_src = THREAD1; msg.hdr.msg_dst = HANDLER; MsgEnQueue((Queue_t*)&MsgQueue, &msg); printf("%s: Thread1 send a message!\n",__FUNCTION__); sleep(1); } } void msg_sender2() { int i = 0; while(1) { if(i > 10) { i = 0; } Msg_t msg; msg.hdr.msg_type = i++; msg.hdr.msg_src = THREAD2; msg.hdr.msg_dst = HANDLER; MsgEnQueue((Queue_t*)&MsgQueue, &msg); printf("%s: Thread2 send a message!\n",__FUNCTION__); sleep(1); } } void msg_sender3() { int i = 0; while(1) { if(i > 10) { i = 0; } Msg_t msg; msg.hdr.msg_type = i++; msg.hdr.msg_src = THREAD3; msg.hdr.msg_dst = HANDLER; MsgEnQueue((Queue_t*)&MsgQueue, &msg); printf("%s: Thread3 send a message!\n",__FUNCTION__); sleep(1); } }

这里我create了三个线程来模拟消息生产者,每个生产者每隔1秒往消息队列里写消息。

五、跑起来看看

先贴完整的代码:
msg_queue.c:
  1 #include <stdio.h>  2 #include <pthread.h>  3 #include <semaphore.h>  4 #include <unistd.h>  5 #include <string.h>  6 #include "msg_def.h"  7   8 Queue_t MsgQueue;  9   10 int main(int argc, char* argv[])  11 {  12     int ret;  13     pthread_t thread1_id;  14  pthread_t thread2_id; 15  pthread_t thread3_id; 16  pthread_t handler_thread_id; 17 18 ret = MsgQueueInit((Queue_t*)&MsgQueue); 19 if(ret != 0) 20  { 21 return -1; 22  } 23 24 if(pthread_create(&handler_thread_id, NULL, (void*)msg_handler, NULL)) 25  { 26 printf("create handler thread fail!\n"); 27 return -1; 28  } 29 30 31 if(pthread_create(&thread1_id, NULL, (void*)msg_sender1, NULL)) 32  { 33 printf("create thread1 fail!\n"); 34 return -1; 35  } 36 37 if(pthread_create(&thread2_id, NULL, (void*)msg_sender2, NULL)) 38  { 39 printf("create thread2 fail!\n"); 40 return -1; 41  } 42 43 if(pthread_create(&thread3_id, NULL, (void*)msg_sender3, NULL)) 44  { 45 printf("create thread3 fail!\n"); 46 return -1; 47  } 48 49 50 while(1) 51  { 52 sleep(1); 53  } 54 55 return 0; 56 } 57 58 59 60 61 int MsgQueueInit(Queue_t* Q) 62 { 63 if(!Q) 64  { 65 printf("Invalid Queue!\n"); 66 return -1; 67  } 68 Q->rear = 0; 69 Q->head = 0; 70 sem_init(&Q->sem, 0, 1); 71 return 0; 72 } 73 74 int MsgDeQueue(Queue_t* Q, Msg_t* msg) 75 { 76 if(!Q) 77  { 78 printf("Invalid Queue!\n"); 79 return -1; 80  } 81 if(Q->rear == Q->head) //only one cosumer,no need to lock head 82  { 83 printf("Empty Queue!\n"); 84 return -1; 85  } 86 memcpy(msg, &(Q->data[Q->head]), sizeof(Msg_t)); 87 Q->head = (Q->head+1)%QUEUE_SIZE; 88 return 0; 89 90 } 91 92 int MsgEnQueue(Queue_t* Q, Msg_t* msg) 93 { 94 if(Q->head == (Q->rear+1)%QUEUE_SIZE) 95  { 96 printf("Full Queue!\n"); 97 return -1; 98  } 99 sem_wait(&Q->sem); 100 memcpy(&(Q->data[Q->rear]), msg, sizeof(Msg_t)); 101 Q->rear = (Q->rear+1)%QUEUE_SIZE; 102 sem_post(&Q->sem); 103 return 0; 104 } 105 106 void msg_printer(Msg_t* msg) 107 { 108 if(!msg) 109  { 110 return; 111  } 112 printf("%s: I have recieved a message!\n", __FUNCTION__); 113 printf("%s: msgtype:%d msg_src:%d dst:%d\n\n",__FUNCTION__,msg->hdr.msg_type,msg->hdr.msg_src,msg->hdr.msg_dst); 114 115 } 116 117 int msg_send() 118 { 119 120  Msg_t msg; 121 msg.hdr.msg_type = GO_HOME; 122 msg.hdr.msg_src = THREAD1; 123 msg.hdr.msg_dst = HANDLER; 124 return MsgEnQueue((Queue_t*)&MsgQueue, &msg); 125 126 } 127 128 void msg_handler() 129 { 130 sleep(5); //let's wait 5s when starts 131 while(1) 132  { 133  Msg_t msg; 134 memset(&msg, 0 ,sizeof(Msg_t)); 135 int res = MsgDeQueue((Queue_t*)&MsgQueue, &msg); 136 if(res != 0) 137  { 138 sleep(10); 139 continue; 140  } 141 msg_printer(&msg); 142 sleep(1); 143  } 144 } 145 146 147 void msg_sender1() 148 { 149 int i = 0; 150 while(1) 151  { 152 if(i > 10) 153  { 154 i = 0; 155  } 156  Msg_t msg; 157 msg.hdr.msg_type = i++; 158 msg.hdr.msg_src = THREAD1; 159 msg.hdr.msg_dst = HANDLER; 160 MsgEnQueue((Queue_t*)&MsgQueue, &msg); 161 printf("%s: Thread1 send a message!\n",__FUNCTION__); 162 sleep(1); 163  } 164 } 165 166 void msg_sender2() 167 { 168 int i = 0; 169 while(1) 170  { 171 if(i > 10) 172  { 173 i = 0; 174  } 175  Msg_t msg; 176 msg.hdr.msg_type = i++; 177 msg.hdr.msg_src = THREAD2; 178 msg.hdr.msg_dst = HANDLER; 179 MsgEnQueue((Queue_t*)&MsgQueue, &msg); 180 printf("%s: Thread2 send a message!\n",__FUNCTION__); 181 sleep(1); 182  } 183 } 184 185 void msg_sender3() 186 { 187 int i = 0; 188 while(1) 189  { 190 if(i > 10) 191  { 192 i = 0; 193  } 194  Msg_t msg; 195 msg.hdr.msg_type = i++; 196 msg.hdr.msg_src = THREAD3; 197 msg.hdr.msg_dst = HANDLER; 198 MsgEnQueue((Queue_t*)&MsgQueue, &msg); 199 printf("%s: Thread3 send a message!\n",__FUNCTION__); 200 sleep(1); 201  } 202 }

msg_def.h:

 1 #include <stdio.h>  2 #include <pthread.h>  3 #include <semaphore.h>  4   5 typedef unsigned char uint8;  6 typedef unsigned short unit16;  7 typedef unsigned int uint32;  8   9 #define QUEUE_SIZE 1000  
10   
11 typedef struct Msg_Hdr_s  
12 {  
13  uint32 msg_type; 14  uint32 msg_len; 15  uint32 msg_src; 16  uint32 msg_dst; 17 }Msg_Hdr_t; 18 19 typedef struct Msg_s 20 { 21  Msg_Hdr_t hdr; 22 uint8 data[100]; 23 } Msg_t; 24 25 typedef struct Queue_s 26 { 27 int head; 28 int rear; 29  sem_t sem; 30  Msg_t data[QUEUE_SIZE]; 31 }Queue_t; 32 33 typedef struct Queue_s QueueNode; 34 35 enum MSG_TYPE 36 { 37  GO_HOME, 38  GO_TO_BED, 39  GO_TO_LUNCH, 40  GO_TO_CINAMA, 41  GO_TO_SCHOOL, 42  GO_DATEING, 43 GO_TO_WORK,//6 44 }; 45 46 enum SRC_ADDR 47 { 48  THREAD1, 49  THREAD2, 50  THREAD3, 51  HANDLER, 52 }; 53 54 55 int MsgQueueInit(Queue_t* Q); 56 int MsgDeQueue(Queue_t* Q, Msg_t* msg); 57 int MsgEnQueue(Queue_t* Q, Msg_t* msg); 58 void msg_handler(); 59 void msg_sender1(); 60 void msg_sender2(); 61 void msg_sender3(); 62 void msg_printer(Msg_t* msg); 63 int msg_send();

看看跑起来的现象:
Finish!
现在这套进程内的消息队列的架构在实际工程中非常实用(当然实际工程的框架会复杂健壮得多),很多工程都需要这种基于事件推动的思想来保证每条请求都可以有条不絮地执行,所以这个框架也是有用武之地的,尤其配合状态机非常适合!

相关文章:

threshold 二值化的实现

#include "opencv2/imgproc/imgproc.hpp" #include "opencv2/highgui/highgui.hpp" int main( ) {// 读取源图像及判断cv::Mat srcImage cv::imread("..\\images\\hand1.jpg");if( !srcImage.data ) return 1;// 转化为灰度图像cv::Mat srcGray…

如何定时备份数据库并上传七牛云

前言&#xff1a; 这篇文章主要记录自己在备份数据库文件中踩的坑和解决办法。 服务器数据库备份文件之后上传到七牛云 备份数据库文件在服务器根目录下 创建 /backup/qiniu/.backup.sh #!/bin/bash# vuemall 数据库名称 # blog_runner vuemall 的管理用户# admin vuem…

【OpenCV 】计算物体的凸包/创建包围轮廓的矩形和圆形边界框/createTrackbar添加滑动条/

目录 topic 1:模板匹配 topic 2:图像中寻找轮廓 topic 3:计算物体的凸包 topic 4:轮廓创建可倾斜的边界框和椭圆 topic 5:轮廓矩 topic 6:为程序界面添加滑动条 3.1 目标 3.2 代码实例1 3.3 代码实例2 3.4 实例3运行结果 3.5 运行结果 topic 1:模板匹配 topic 2:图…

开源:Angularjs示例--Sonar中项目使用语言分布图

在博客中介绍google的Angularjs 客户端PM模式框架很久了&#xff0c;今天发布一个关于AngularJs使用是简单示例SonarLanguage(示例位于Github&#xff1a;https://github.com/greengerong/SonarLanguage)。本项目只是一个全为客户端的示例项目。项目的初始是我想看看在公司的项…

adaptiveThreshold 阈值化的实现

#include "opencv2/imgproc/imgproc.hpp" #include "opencv2/highgui/highgui.hpp" int main( ) {// 图像读取及判断cv::Mat srcImage cv::imread("..\\images\\hand1.jpg");if( !srcImage.data ) return 1;// 灰度转换cv::Mat srcGray;cv::cvt…

hashMap传入参数,table长度为多少

前言 我的所有文章同步更新与Github--Java-Notes,想了解JVM&#xff0c;HashMap源码分析&#xff0c;spring相关&#xff0c;剑指offer题解&#xff08;Java版&#xff09;&#xff0c;可以点个star。可以看我的github主页&#xff0c;每天都在更新哟。 邀请您跟我一同完成 rep…

【OpenCV】图像/视频相似度测量PSNR( Peak signal-to-noise ratio) and SSIM,视频/图片转换

目录 1 目标 2 原理 2.1 图像比较 - PSNR and SSIM 3 代码 3.1如何读取一个视频流&#xff08;摄像头或者视频文件)&#xff1f; 3 运行效果 视频/图片转换&#xff1a; 如何用OpenCV创建一个视频文件用OpenCV能创建什么样的视频文件如何释放视频文件当中的某个颜色通道…

struts2提交list

2019独角兽企业重金招聘Python工程师标准>>> Action: private List<User> users; jsp: <input type"text" name"users[0].name" value"aaa" /> <input type"text" name"users[1].name" value&q…

双阈值法的实现

#include "opencv2/imgproc/imgproc.hpp" #include "opencv2/highgui/highgui.hpp" int main( ) {// 图像读取及判断cv::Mat srcImage cv::imread("..\\images\\hand1.jpg");if( !srcImage.data ) return 1;// 灰度转换cv::Mat srcGray;cv::cvt…

设计模式 小记

观察者模式 1.被观察者是单例模式。 生成这模式 1.Director中对于Builder的引用不一定是Strong&#xff0c;根据情况也有可能是Copy。 2.Director 聚合 Builder&#xff0c; 所以Builder本身可以单独拿出来使用。 转载于:https://juejin.im/post/5ca8c24df265da3094116c18

【MATLAB】————matlab raw图转bmp实现

image_path [layer_3_list_folder,\,layer_3_list_name]; img_raw_path fopen(image_path,r);%%打开图像 img_raw fread(img_raw_path,[Width,Height],uint16);% uchar为无符号字符型 mg_raw uint8(img_raw);%%unit8表示无符号整数&#xff0c;范围0-255&#xff0c;u…

人工手动冷备不完全恢复介绍(purge表不完全恢复)

不完全恢复不完全恢复的基本类型&#xff1a;1&#xff09;基于时间点 &#xff08;until time): 使整个数据库恢复到过去的一个时间点前2&#xff09;基于scn &#xff08;until change&#xff09;&#xff1a; 使整个数据库恢复到过去的某个SCN前3&#xff09;基于cancel (u…

半阈值法的实现

#include "opencv2/imgproc/imgproc.hpp" #include "opencv2/highgui/highgui.hpp" #include <iostream> using namespace std; using namespace cv; int main( ) {// 读取源图像及判断cv::Mat srcImage cv::imread("..\\images\\hand1.jpg&q…

novaclient的api调用流程与开发

novaclient的api调用流程与开发 2015年07月05日 19:27:17 qiushanjushi 阅读数&#xff1a;3915 http://blog.csdn.net/tpiperatgod/article/details/18375387?utm_sourcetuicool 另一个地址&#xff1a;http://neuromancer.sinaapp.com/?p64 从nova client的入口查看 cat /u…

【C++】 保存内容到文件工具

1. c 输出到文件 // save mean distance between center and descriptorsstd::string filename configuration_.debug_output_path() "ref_max_mean_distance.txt";FILE* fp fopen(filename.c_str(), "w");try {if (fp nullptr) {return error::Failed…

你知道实习对你有多重要吗?

大学生就业一直是个永恒不变的话题&#xff0c;在过去几年中&#xff0c;每​‌‌次临近毕业季&#xff0c;我们肯定会一次次的听到“史上最难就业季”之说。而每一年的数据也会不断突破前一年的数字。在国新办举行的新闻发布会上&#xff0c;人力资源社会保障部部长尹蔚民提到…

灰度直方图的实现

#include <opencv2\opencv.hpp> int main() {// 图像源获取及判断 cv::Mat Image, ImageGray;Image cv::imread("..\\images\\flower3.jpg"); if(Image.empty()) return -1;cv::imshow("Image",Image);// 转换为灰度图像cv::cvtColor(Image,Image…

Android笔记之ViewModel的使用示例

依赖 implementation android.arch.lifecycle:extensions:1.1.1 implementation com.squareup.retrofit2:retrofit:2.5.0 implementation com.squareup.retrofit2:converter-gson:2.5.0 implementation com.squareup.retrofit2:adapter-rxjava2:2.5.0 implementation io.reacti…

vscode配置记录

vscode配置记录 按照官网教程安装好vs:实际上只需要配置launch.json的”program“如下所示 "configurations": [{"name": "(gdb) Launch","type": "cppdbg","request": "launch","program":…

unity 2d 游戏优化之路 遇坑记录

情景说明&#xff1a; unity 出的Android包&#xff0c;在目前一些主流机型跑都没有问题&#xff0c;但是在 小米3 这种比较老的机器上跑&#xff0c;报如下错误 GLSL compilation failed, no infolog provided 起先&#xff0c;我们一直以为是在低端机器上某个特定的GLSL 在这…

自定义直方图实现

#include <opencv2/opencv.hpp> int main() {// 图像获取及判断cv::Mat srcImage cv::imread("..\\images\\flower3.jpg");if( !srcImage.data ) return 1;cv::imshow("srcImage",srcImage);// 灰度转换 cv::Mat srcGray;cv::cvtColor(srcImage,sr…

js数组指定位置添加删除

示例参考&#xff1a;http://www.w3school.com.cn/jsref/jsref_splice.asp转载于:https://www.cnblogs.com/CarryYou-lky/p/10669859.html

【C++】对象实例化/成员函数/成员变量的内存管理

文章目录1. 对象实例化的内存管理总结2.C成员函数在内存中的存储方式3.C类的实例化对象的大小之sizeof()实例一&#xff1a;实例二&#xff1a;实例三&#xff1a;实例四&#xff1a;实例五&#xff1a;实例六&#xff1a;实例七&#xff1a;实例八&#xff1a;实例九&#xff…

HTML form 标签的 enctype 属性

1. enctype 的定义和用法 enctype 属性规定在发送到服务器之前应该如何对表单数据进行编码。 默认地&#xff0c;表单数据会编码为 "application/x-www-form-urlencoded"。 就是说&#xff0c;在发送到服务器之前&#xff0c;所有字符都会进行编码&#xff08;空格转…

灰度直方图均衡化实现

#include <opencv2/opencv.hpp> int main() {cv::Mat srcImage cv::imread("..\\images\\flower3.jpg");if( !srcImage.data ) return 1;cv::Mat srcGray;cv::cvtColor(srcImage, srcGray, CV_BGR2GRAY);cv::imshow("srcGray", srcGray);// 直方图均…

oracle汉化包下载地址

2019独角兽企业重金招聘Python工程师标准>>> https://www.allroundautomations.com/bodyplsqldevreg.html 转载于:https://my.oschina.net/u/3141521/blog/3034655

【C++】C/C++ 中 static 的用法全局变量与局部变量

C/C 中 static 的用法全局变量与局部变量 目录 1. 什么是static? 1.1 static 的引入 1.2 静态数据的存储 2. 在 C/C 中static的作用 2.1 总的来说 2.2 静态变量与普通变量 2.3 静态局部变量有以下特点&#xff1a; 实例 3. static 用法 3.1 在 C 中 3.2 静态类相关…

浅谈C/C++中的static和extern关键字

一.C语言中的static关键字 在C语言中&#xff0c;static可以用来修饰局部变量&#xff0c;全局变量以及函数。在不同的情况下static的作用不尽相同。 (1)修饰局部变量 一般情况下&#xff0c;对于局部变量是存放在栈区的&#xff0c;并且局部变量的生命周期在该语句块执行结束时…

彩色直方图均衡化实现

#include <opencv2/opencv.hpp> int main() {// 图像获取及验证cv::Mat srcImage cv::imread("..\\images\\flower3.jpg");if( !srcImage.data ) return 1;// 存储彩色直方图及图像通道向量cv::Mat colorHeqImage; std::vector<cv::Mat> BGR_plane; …

二、python小功能记录——监听鼠标事件

1.原文链接 #-*- coding:utf-8 -*- from pynput.mouse import Button, Controller## ## 控制鼠标 ## # 读鼠标坐标 mouse Controller() print(The current pointer position is {0}.format(mouse.position)) # 设置鼠标坐标 mouse.position (10, 20) print(No…