semaphore原来还有这一面
semaphore(常被翻译为旗语)是SystemVerilog中的内建类,主要用于实现多进程对于共享资源的协同访问控制。从使用上来说,semaphore类似于一个存储桶,当我们为semaphore分配内存时,会同时创建一定数目的key到存储桶中。存储桶在创建时可以指定一定数目的key,进行协同访问时,需要通过存储桶中key的存放与获取实现交互。一般情况下,必须首先获得一把或多把桶中的key后,后续程序处理完成后,需要将获取的key归还给存储桶,如果此时另一个进程试图获取桶中的资源,必须等到桶中有足够多的key时才能进行,否则其后的程序会一直阻塞。semaphore的语法格式如下:semaphore smp;在semaphore中常用的方法如下表所示。
方法名 | 说明 |
new() | [function]创建一个包含指定数目key的旗语对象 |
get() | [task]从存储桶中获取对应数目的key |
put() | [void function]将指定数目的key放回至存储桶中 |
try_get() | [int function]试图获得指定数目的key而不带阻塞其后程序的执行 |
然而这些方法在使用时,还有很多意想不到的用法,下面将针对这些常用方法的特殊用法示例说明。
1 new()
格式如下:semaphore_name = new(number_of_keys)其中number_of_keys是创建的semaphore存储桶中包含的初始key的数目,其实number_of_keys也可以不指定,默认值为0,说明此时声明的semaphore句柄指向的存储空间中没有key可用,但是并不意味着此时没有为semaphore句柄分配空间。【示例】semaphore创建时不指定key数目
【仿真结果】
示例中,sm1虽然创建了对象,但是在创建时并没有指定其中存储的key的数目,所以在11行从sm1中没有获取到key,从而仿真输出“Get zero key”,并且从示例中,可以看到sm2在没有进行创建时,其指向为null。
2 get()
格式如下:semaphore_name.get(number_of_keys);或者semaphore_name.get();该方法被调用时,如果没有指定number_of_keys,那么get()方法默认需要获取的key数目为1。如果在get时,存储桶中有足够多的key可供获取,那么方法将返回并且程序继续执行,但是如果存储桶中没有足够多的key可供get()方法获取,那么进程将被阻塞。然而实际使用时,经常会出现get()没有获取到匹配的key,进程未被阻塞且提前退出的情况,这到底为什么呢?请看下例。【示例】 【仿真结果】
纳尼?没有get()到足够的key,进程应该一直阻塞么,为什么没有阻塞直接程序结束了呢?首先sm在new的时候在存储桶中初始化了2个key,当前进程中的sm.get(3)需要获取到3个key,此时并没有其他进程向该sm存储桶中存放额外的key,也就是说此时存储桶中仅仅只有2个key,当执行sm.get(3)时,sm会认为该任务不能完成所以提前结束,从而其后被阻塞的程序将不会被执行到,也就是说,在使用semaphore的get()任务时,需要保证semaphore中存放了满足get()要求数目的key,否则,执行get()任务后并不会阻塞其后的程序,而是直接结束当前的进程。上述示例的操作过程如下图所示:
【示例】多进程调用get()任务
【仿真结果】
示例中,与sm进行通信的进程有process-1和process-2,其中process-2要获取到2个key,process-1仅需要1个key,尽管process-1执行完后会返回1个key给存储桶,但是此时与sm进行通信的进程只剩下process-2,而process-2中的sm.get(2)查询到sm中仅有1个key,不能满足其要求,所以程序会提前结束并不会一直阻塞等下去,其后被阻塞的语句将不会被执行。这里还需要注意,尽管process-1和process-2是并行执行的,但是因为process-1仅需要1个key,而process-2需要2个key,在SystemVerilog中会先满足process-1的要求,即先处理process-1的进,所以先进先出的规则对于semaphore实际上并不适用。上述示例的操作过程如下图所示:
如果所有与semaphore进行通信的进程返回给存储桶的key满足与semaphore通信的进程对于key获取的需要,那么get()方法将会一直阻塞直到获取到足够的key,如下例所示。【示例】
【仿真结果】
示例中,与sm进行通信交互的进程有三个,分别是process-1、process-2和process-3。其中process-1因为只有1个key的要求,并且满足sm此时存储桶中key的初始数目,在process-1执行完后,给sm存储桶中返回了2个key,此时排队的process-2和process-3分别需要获取2个key和3个key,因为存储桶中此时只有process-1执行完返回的2个key,所以process-2匹配了获取key的数目,process-3需要三个不满足继续挂起等待,又因为此时与sm通信的进程还有process-2和process-3,所以此时process-3仍将处于挂起等待的状态不会结束,并且等待process-2执行完后,sm存储桶中最终剩下的key数目。process-2执行完毕后,向key存储桶中返回了3个key,此时挂起等待的process-3捕获到该消息,匹配了其对于key的需求,所以被阻塞的process-3不再阻塞,其后的程序获取到足够的key后正常执行。上述示例的操作过程如下图所示:
3 put()
格式如下:semaphore_name.put(number_of_keys);或者semaphore_name.put();当put()被调用时会返回给存储桶指定数目的key,如果方法调用时不指定key的数目,默认返回1个key。这里需要注意:put返回存储桶的key的数目可以比其get到的key的数目多,如下示例。【示例】put回key的数目大于 get到的key的数目 【仿真结果】
示例中,第7行get到1个key之后,在第9行put回了2个key到存储桶中,此时存储桶中就有2个key而不是在sm进行创建时指定的初始值1,所以第11行可以从存储桶中获取到2个key。示例也说明了,semaphore在创建时指定的key的数目实际上只是一个初始值,在后续的程序中是可以增加修改的。上述示例的操作过程如下图所示:
除此之外,在具体使用时,在没有get到key的情况,也是可以使用put向存储桶返回特定数目的key。【示例】
【仿真结果】
示例中,sm创建时初始化了1个key,紧接着在第7行又向其中put了2个key,此时存储桶中就有3个key,所以在第9行sm.get(3)的时候程序不会阻塞,因为此时sm.get(3)方法从存储桶中可以获取到匹配的key数目。即在具体使用时,在没有get到key的情况,也是可以使用put向存储桶返回特定数目的key。上述示例的操作过程如下图所示:
这里需要大家注意,如果semaphore在创建对象的时候不指定初始化key的数目,可以在后续使用的过程中通过put方法向semaphore存储桶中放入相应的key的数目从而间接实现对于存储桶key的初始化。
4 try_get()
格式如下:semaphore_name.try_get(number_of_keys);或者semaphore_name.try_get();try_get()函数与get()任务不同点是,get()方***阻塞其后程序的执行并且在获取不到期望数目的key时会提前退出当前进程,而try_get()函数如果semaphore存储桶中有足够的key,则会获取到对应数目的key,并返回1,程序继续执行,当获取不到期望数目的key时,try_get()函数会返回0,并且try_get()函数不会阻塞进程的继续执行。【示例】 【仿真结果】
示例中,虽然创建了sm,但是并未在其中初始化任何数目的key,在0时刻process-1和process-2中的try_get()从sm中虽然没有获取到任何key,但是并没有阻塞其后语句的执行。上述示例的操作过程如下图所示:
通过上述示例对于semaphore方法的说明,大家在使用时注意以下几点:
- 没有使用new()创建的semaphore句柄指向null;
- 使用new创建semaphore对象时,可以不指定初始化key的数目,此时如果要使用该semaphore,需要使用put方法向其中放入期望的key的数目;
- 如果与semaphore进行交互的所有进程执行完了并且返回了所有的key,此时如果期望使用get()方法获取的key的数目大于semaphore存储桶中key的数目,那么get()方法将不会阻塞程序的执行而是直接退出当前进程;
- try_get()方法无论semaphore中是不是有key都不会阻塞其后程序的执行;