SV--线程(mailbox)

3 mailbox

3.1概述

•    线程之间如果传递信息,可以使用mailbox。
•    mailbox和队列queue有相近之处。

•    mailbox是一个对象,因此也可以用new()来例化,例化时,有一个可选的参数size来限定其存储的最大数量,如果size是0或者没有指定,则信箱是无限大的,可以容纳任意多的条目。

•    使用put()可以把数据放入mailbox, 使用get()可以从信箱移除数据。
•    如果信箱为满,则put()会阻塞;如果信箱为空,则get()会阻塞。
•    peek()可以获取对信箱里数据的拷贝而不移除它。
•    线程之间的同步方法需要注意,哪些是阻塞方法,哪些是非阻塞 方法,即哪些是立即返回的,而哪些可能需要等待时间的。

3.2代码演练 

program automatic bounded;
mailbox mbx;
initial begin
mbx = new(l); //容量为1 
fork 
// Producer线程
for (int i=l; i<4; i++) begin
$display("Producer: before put(%0d)", i); 
mbx.put(i);
$display("Producer: after put(%0d}", i);
end 
// Consumer线程
repeat(4) begin
int j;
#lns mbx.get(j); 
$display("Consumer:after get(%0d}", j);
end
join
end
endprogram

Producer: before put(1)
Producer: after put(1)

Producer: before put(2)

Consumer: after get(1)

Producer: after put(2)

Producer: before put(3)

Consumer: after get(2)

Producer: after put (3)

Consumer: after get(3) 

3.3实例演练

对于一辆车子而言,如果要实时显示这辆车子的状态,会需要多个仪表。显示的参数包括车速、油量、发动机的转速和温度等, 这些参数涉及到了各个传感器到汽车控制中枢的通信。

如果我们继续通过上面这辆BYD, 来模拟不同传感器(线程)到车的中央显示的通信,可以利用SV的mailbox (信箱)来满足多个线程之间的数据通信。

class car;
mailbox tmp_mb, spd_mb, fuel_mb; 
int sample_period;
function new(); 
sample_period = 10; 
tmp_mb = new(); 
spd_mb = new(); 
fuel_mb = new(); 
endfunction

task sensor_tmp;
int tmp; 
forever begin
std::randomize(tmp) with {tmp >= 80 && tmp <= 100;}; 
tmp_mb.put(tmp);
#sample_period; 
end 
endtask 

task sensor_spd, 
int spd; 
forever begin 
std::randomize(spd) with {spd>= 50 && spd <=60;};
spd_mb.put(spd),
#sample_period;
end 
endtask

task sensor_fuel;
int fuel;
forever begin
Std::randomize (fuel) with {fuel>= 30 && fuel <= 35;}; 
fuel_mb.put(fuel); 
#sample_periode;
end 
endtask 

task drive () ;
fork
sensor_tmp (); 
sensor_spd (); 
sensor_ fuel (); 
display(tmp_mb, "temperature"); 
display(spd_mb, "speed"); 
display(fuel_mb, "feul"); 
join_none 
endtask

task display(mailbox_mb, string name="mb") ;
int val;
forever begin
mb.get(val): 
$display ("car: %s is %0d", name, val)
end
endtask
endclass

module road; 
car byd = new(); 
initial begin 
byd. drive(); 
end 
endmodule 

 -    # car:: temperature is 100
-    # car:: speed is 50
-    # car:: feul is 30
-    # car:: temperature is 96
-    # car:: speed is 55 
-    # car:: feul is 33
-    # car:: temperature is 89
-    # car:: speed is 57 
-    # car:: feul is 31

对于mailbox的用法,与FIFO的使用很相似。如果我们将上面的mailbox用队列来替代的话,则可以修改为下面的例码。 

class car;
//mailbox tmp_mb, spd_mb, fuel_mb; 
int tmp_q[$],spd_q[$],fuel_q[$];
int sample_period;
function new(); 
sample_period = 10; 
//tmp_mb = new(); 
//spd_mb = new(); 
//fuel_mb = new(); 
endfunction

task sensor_tmp;
int tmp; 
forever begin
std::randomize(tmp) with {tmp >= 80 && tmp <= 100;}; 
//tmp_mb.put(tmp);
tmp_q.push_back(tmp);
#sample_period; 
end 
endtask 

task sensor_spd, 
int spd; 
forever begin 
std::randomize(spd) with {spd>= 50 && spd <=60;};
//spd_mb.put(spd);
spd_q.push_back (spd);
#sample_period;
end 
endtask

task sensor_fuel;
int fuel;
forever begin
Std::randomize (fuel) with {fuel>= 30 && fuel <= 35;}; 
//fuel_mb.put(fuel); 
fuel_q.push_back(fuel);
#sample_periode
end 
endtask 

task drive () ;
fork
sensor_tmp (); 
sensor_spd (); 
sensor_ fuel (); 
display(tmp_mb, "temperature"); 
display(spd_mb, "speed"); 
display(fuel_mb, "feul"); 
join_none 
endtask

task display(string name, ref int q[$]);
int val;
forever begin
//mb.get(val);
wait(q.size() > 0) ; 
val = q.pop_front(); 
$display ("car: : %s is %0d", name,val);
//$display ("car: %s is %0d", name, val);
end
endtask
endclass

module road; 
car byd = new(); 
initial begin 
byd. drive(); 
end 
endmodule 

通过队列改造来完成进程间通信的例子, 我们可以找出maibox与queue在使用时的差别:

maibox必须通过new()例化,而队列只需要声明。

mailbox可以将不同的数据类型同时存储,不过这么做是不建议的,对于队列来讲,它内部存储的元素类型必须一致。

mailbox 的存取方式put()和get()是阻塞方式,即使用它们时,方法不一定会立即返回,而队列所对应的存取方式,push_back()和pop_front()方法是非阻塞的,会立即返回。因此在使用queue取数时,需要额外填写wait(queue.size()>0)才可以在其后对非空的queue做取数操作,此外也应该注意,如果要调用阻塞方法,那么只可以在task中调用,因为阻塞方法是耗时的,而调用非阻塞方法,例如,queue中的push_back()和pop_front()方法,则既可以在task又可以在function中。

mailbox只能够用作FIFO,而queue除了按照FIFO使用,还有其它应用的方式例如LIFO(last in first out)

对于mailbox变量的操作,在传递形式参数时,实际传递并拷贝的是mailbox的指针,而在第二个例子中的task display(),关于queue的形式参数声明是ref方向,因为如果采用默认的input方向,那么传递过程中发生是数组的拷贝,以至于方向内部对queue的操作并不会影响外部的queue本身,因此在传递数组时,大家应该考虑到,对数组做的是引用还是拷贝,进而考虑端口的声明方向。

•    mailbox在例化时,通过new(N)的方式可以使其变为定长 (fixed length)容器。这样在负载到长度N以后,无法再对其写入。如果用new()的方式,则表示信箱容量不限大小。
•    除了put()/get()/peek()这样的阻塞方法,用户也可以考虑使用try _put()/try _get()/try _peek()等非阻塞方法。
•    如果要显式地限定mailbox中元素的类型,可以通过mailbox #(type = T)的方式来声明。例如上面的三个mailbox存储的是int, 则可以在声明时进一步限定其类型为mailbox #(int)。

3.4 event semaphore mailbox对同一功能的实现代码比较 

•    除了上面的三种常见需求, 有时候, 进程之间也需要同步。 这里我们来考虑, 如果要对这辆车熄火(stall)的话, 得先检查是否车挂挡在P (park) , 进而再将车钥匙拔出。
•    我们可以将stall和park两个线程的同步视作, 先由stall发起同步请求, 再等待park线程完成并响应同步请求, 最后由stall线程继续其余的程序, 最终结束熄火的过程。
•    我们不妨用之前掌握的SV三种进程通信的方式event、semaphore和mailbox来解决进程间的同步问题。

event代码实现: 

class car;
event e_stall; 
event e_park; 

task stall;
$display ( "car::stall started"); 
#lns; 
-> e_stall;
@e_park; 
$display ( "car::stall finished") ; 
endtask

task park;
@e_stall;
$display ("car: : park started"); 
#lns; 
-> e_park; 
$display("car::park finished"); 
endtask

task drive() ;
fork
this.stall(); 
this.park(); 
join_none 
endtask 
endclass 

semaphore 代码实现:

class car;
semaphore key; 
function new(); 
key= new(0); 
endfunction 

task stall; 
$display("car::stall started"); 
#lns; 
key.put(); 
key.get(); 
$display("car::stall finished");
endtask

task park;
key.get() ; 
$display("car: :park started"); 
#lns; 
key.put() ; 
$display("car: :park finished"); 
endtask

task drive();
fork
this.stall();
this.park();
join_none 
endtask 
endclass 

mailbox代码实现:


task stall;
int val = 0; 
$display ("car:: stall started"); 
#lns; 
mb.put(val);
mb.get(val); 
$display("car::stall finished"); 
endtask

task park;
int val = 0; 
mb.get(val);
$display ("car::park started"); 
#lns; 
mb.put(val); 
$display("car::park finished");
endtask

task drive();
fork
this.stall();
this.park();
join_none 
endtask 
endclass
 

•    上面三种用来实现线程A请求同步线程B, 线程B再晌应线程A的同步 方式输出结果保持一致。
•    从这三段代码可以看出, 用来做线程同步的选择也有多种。

•    如果要在同步(事件)的同时, 完成一些数据传输, 那么更合适的是mailbox, 因为它可以用来存储一些数据,而event和semaphore更偏向于小信息量的同步, 即不包含更多的数据信息。
# car::stall started
# car::park started 
# car::park finished 
# car::stall finished 

event: 最小信息量的触发,即单一的通知功能。可以用来做事件的触发, 也可以多个event组合起来用来做线程之间的同步。

semaphore: 共享资源的安全卫士。如果多线程间要对某一公共资源做访问,即可以使用这个要素。

mailbox: 精小的SV原生FIFO。在线程之间做数据通信或者内部数 据缓存时可以考虑使用此元素。

 

  • 34
    点赞
  • 61
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
东南亚位于我国倡导推进的“一带一路”海陆交汇地带,作为当今全球发展最为迅速的地区之一,近年来区域内生产总值实现了显著且稳定的增长。根据东盟主要经济体公布的最新数据,印度尼西亚2023年国内生产总值(GDP)增长5.05%;越南2023年经济增长5.05%;马来西亚2023年经济增速为3.7%;泰国2023年经济增长1.9%;新加坡2023年经济增长1.1%;柬埔寨2023年经济增速预计为5.6%。 东盟国家在“一带一路”沿线国家中的总体GDP经济规模、贸易总额与国外直接投资均为最大,因此有着举足轻重的地位和作用。当前,东盟与中国已互相成为双方最大的交易伙伴。中国-东盟贸易总额已从2013年的443亿元增长至 2023年合计超逾6.4万亿元,占中国外贸总值的15.4%。在过去20余年中,东盟国家不断在全球多变的格局里面临挑战并寻求机遇。2023东盟国家主要经济体受到国内消费、国外投资、货币政策、旅游业复苏、和大宗商品出口价企稳等方面的提振,经济显现出稳步增长态势和强韧性的潜能。 本调研报告旨在深度挖掘东南亚市场的增长潜力与发展机会,分析东南亚市场竞争态势、销售模式、客户偏好、整体市场营商环境,为国内企业出海开展业务提供客观参考意见。 本文核心内容: 市场空间:全球行业市场空间、东南亚市场发展空间。 竞争态势:全球份额,东南亚市场企业份额。 销售模式:东南亚市场销售模式、本地代理商 客户情况:东南亚本地客户及偏好分析 营商环境:东南亚营商环境分析 本文纳入的企业包括国外及印尼本土企业,以及相关上下游企业等,部分名单 QYResearch是全球知名的大型咨询公司,行业涵盖各高科技行业产业链细分市场,横跨如半导体产业链(半导体设备及零部件、半导体材料、集成电路、制造、封测、分立器件、传感器、光电器件)、光伏产业链(设备、硅料/硅片、电池片、组件、辅料支架、逆变器、电站终端)、新能源汽车产业链(动力电池及材料、电驱电控、汽车半导体/电子、整车、充电桩)、通信产业链(通信系统设备、终端设备、电子元器件、射频前端、光模块、4G/5G/6G、宽带、IoT、数字经济、AI)、先进材料产业链(金属材料、高分子材料、陶瓷材料、纳米材料等)、机械制造产业链(数控机床、工程机械、电气机械、3C自动化、工业机器人、激光、工控、无人机)、食品药品、医疗器械、农业等。邮箱:market@qyresearch.com

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

创芯人

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值