在许多情况下,通过邮箱连接的两个线程应该是一致的,这样生产者就不会跑到消费者面前。该方法的优点是,它可以使生成激励的整个链在运行过程中保持一致。最高级别的发生器需要等到底部的最后一件事才能结束。
如果生产者和消费者的步骤相同,则需要额外的握手信号。如下所示,生产者和消费者是两类,使用邮箱交换整数,但两者之间没有明显的同步信号。直到生产者结束,消费者才开始。
program automatic unsynchronized; mailbox mbx; class Producer; task run(); for (int i=1;i<4;i ) begin $display("Producer:before put( )",i); mbx.put(i); end endtask endclass class Consumer; task run(); int i; repeat (3) begin mbx.get(i); $display("Consumer:after get( )",i); end endtask endclass Producer p; Consumer c; initial begin mbx=new(); p=new(); c=new(); fork p.run(); c.run(); join end endprogram
如上所示,在消费者开始阅读之前,制造商已经将三个整数放入邮箱中。这是因为当线程没有遇到阻塞句时,它将继续运行。输出如下:
Producer: before put(1) Producer: before put(2) Producer: before put(3) Consumer: after get(1) Consumer: after get(2) Consumer: after get(3)
它会导致先完全放入后取出的情况,而不是同步。为此,介绍了消费者和生产者同步操作的三种方法。
使用定容信箱和peek
program automatic synch_peek; mailbox mbx; class Consumer; task run(); int i; repeat(3) begin mbx.peek(i); $display("Consumer:after get( )",i); mbx.get(i); end endtask endclass Producer p; Consumer c; initial begin mbx=new(1); p=new(); c=new(); fork p.run(); c.run(); join end endprogram
若消费者使用get()替代peek()启动循环,然后事物会立即从邮箱中移出,这样生产者就可以在消费者完成事物处理之前生成新的数据。虽然可以看出,生产者和消费者的步伐是一致的,但生产者仍然比消费者提前一件事,因为容量为1的固定容器只有在你对第二件事进行put堵塞发生在操作过程中。
Producer:before put(1) Producer:before put(2) Consumer:after get(1) Consumer:after get(2) Producer:before put(3) Consumer:after get(3)
使用信箱事件完成线程同步
你想让两个线程使用握手信号,这样制造商就不会超过消费者。由于消费者等待制造商以阻塞的方式使用邮箱,制造商也可以阻塞等待消费方式完成邮箱条目的处理。
program automatic mbx_evt; mailbox mbx; event handshake; class Producer; task run; for(int i=1;i<4;i ) begin $display("Producer:before put( )",i); mbx.put(i); @handshake; $display("Producer:after put( )",i); end endtask endclass class Consumer; task run; int i; repeat(3) begin mbx.get(i); $display("Consumer:after get( )",i); ->handshake end endtask endclass Producer p; Consumer c; initial begin mbx=new(); p=new(); c=new(); fork p.run(); c.run(); join end endprogram
这样,生产者和消费者在运行过程中成功同步,因为生产者在阅读旧值之前不会产生新值。
使用两个信箱同步线程
program automatic mbx_evt; mailbox mbx,rtn; class Producer; task run; int k; for(int i=1;i<4;i ) begin $display("Producer:before put( )",i); mbx.put(i); rtn.get(k); //@handshake; $display("Producer:after put( )",i); end endtask endclass class Consumer; task run; int i; repeat(3) begin $display("Consumer:before get"); mbx.get(i); $display("Consumer:after get( )",i); rtn.put(-i); end endtask endclass Producer p; Consumer c; initial begin mbx=new(); p=new(); c=new(); fork p.run(); c.run(); join end endprogram
事实上,它类似于使用事件模式,但信箱可以在这里传递信息rtn信箱中的信息只是原始整数的相反值,便于调试。
其它同步技术
握手也可以通过变量或旗语阻塞线程。事件是最简单的结构,其次是变量。旗语相当于第二个邮箱,但没有信息交换。SV的定容信箱技术较差,原因是无法在生产方放入第一个事务的时候让它阻塞,生产方一直比消费方提前一个事务的时间。