事件集
我们一般都使用过 P2P 软件(如 BT、电骡等)下载过电影等,P2P 软件将需要下载的文件划分为许多个小片断,它从多个文件源下载这些不同的文件片断,当所有的文件片断下载完毕后,P2P 软件再将它们组装成一个文件:
P2P 软件需要记录这些文件片断的下载情况和实现在线播放的功能,SylixOS 提供的事件集这一线程间通信手段很好地解决了这些问题。
事件集被定义为 ULONG 型,每一位代表一个事件,对于上面的例子,每一位代表一个文件片断,这样就很好地解决了文件片断下载情况记录的问题。
而在线播放功能依赖于当前需要播放的文件片断,如果当前需要播放的文件片断未下载完,那么只能暂停播放了。下载和播放使用不同线程实现。下载线程不能简单地下载一个文件片断就唤醒播放线程进行播放,因为能下载到哪些文件片断是不确定的,当前下载到的文件片断不一定是播放线程当前需要的。事件集提供了发送和等待事件的 API,很好地解决了下载和播放线程同步的问题。
这里举了 P2P 软件的例子来说明事件集的用途,实际上事件集的功能不限于此。
一个 SylixOS 事件集必须要调用 Lw_Event_Create 函数创建之后才能使用,如果创建成功,Lw_Event_Create 函数会返回一个事件集的句柄。
线程如果需要等待事件,可以调用 Lw_Event_Wait 函数,中断服务程序不能调用 Lw_Event_Wait 函数等待事件,因为 Lw_Event_Wait 函数在事件无效时会阻塞当前线程。
中断服务程序可以使用 Lw_Event_TryWait 函数尝试等待事件,Lw_Event_TryWait 函数在事件无效时会立即返回,不会阻塞当前线程。
发送事件可以调用 Lw_Event_Send 函数。
当一个事件集使用完毕后(并确保以后也不再使用),应该调用 Lw_Event_Delete 函数删除它,SylixOS 会回收该事件集占用的内核资源。
事件集创建和删除
#include <SylixOS.h>
LW_HANDLE Lw_Event_Create(CPCHAR pcName,
ULONG ulInitEvent,
ULONG ulOption,
LW_OBJECT_ID *pulId);
函数 Lw_Event_Create 原型分析:
- 此函数成功时返回事件集的句柄,失败时返回 LW_HANDLE_INVALID 并设置错误号。
- 参数 pcName 是事件集的名字。
- 参数 ulInitEvent 是事件集的初始值。
- 参数 ulOption 是事件集的创建选项。
- 输出参数 pulId 用于接收事件集的 ID。
事件可以用宏 LW_OPTION_EVENT_n(n 范围从 0 到 31)的组合,如果是所有事件,可以用宏 LW_OPTION_EVENT_ALL。
#include <SylixOS.h>
ULONG Lw_Event_Delete(LW_HANDLE *pulId);
函数 Lw_Event_Delete 原型分析:
- 此函数成功时返回 ERROR_NONE,失败时返回错误号。
- 参数 pulId 是接收事件集的句柄。
事件集发送
#include <SylixOS.h>
ULONG Lw_Event_Send(LW_HANDLE ulId,
ULONG ulEvent,
ULONG ulOption);
函数 Lw_Event_Send 原型分析:
- 此函数成功时返回 ERROR_NONE,失败时返回错误号。
- 参数 ulId 是事件集的句柄。
- 参数 ulEvent 是需要发送的事件。
- 参数 ulOption 是事件发送的选项,如下表所示。
宏名 | 含义 |
---|---|
LW_OPTION_EVENTSET_SET | 将指定事件设为 1 |
LW_OPTION_EVENTSET_CLR | 将指定事件设为 0 |
事件集等待
#include <SylixOS.h>
ULONG Lw_Event_Wait(LW_HANDLE ulId,
ULONG ulEvent,
ULONG ulOption,
ULONG ulTimeout);
ULONG Lw_Event_WaitEx(LW_HANDLE ulId,
ULONG ulEvent,
ULONG ulOption,
ULONG ulTimeout,
ULONG *pulEvent);
ULONG Lw_Event_TryWait(LW_HANDLE ulId,
ULONG ulEvent,
ULONG ulOption);
ULONG Lw_Event_TryWaitEx(LW_HANDLE ulId,
ULONG ulEvent,
ULONG ulOption,
ULONG *pulEvent);
以上函数原型分析:
- 函数成功时返回 ERROR_NONE,失败时返回错误号。
- 参数 ulId 是事件集的句柄。
- 参数 ulEvent 是需要等待的事件。
- 参数 ulOption 是等待事件的选项,如下表所示。
- 参数 ulTimeout 是等待的超时时间,单位为时钟嘀嗒 Tick。
- 输出参数 pulEvent 标识了接收到的事件。
Lw_Event_TryWait 和 Lw_Event_Wait 的区别在于,如果当前需要等待的事件无效,Lw_Event_TryWait 会立即退出,并返回 ERROR_THREAD_WAIT_TIMEOUT,而 Lw_Event_Wait 则会阻塞直至被唤醒。
宏名 | 含义 |
---|---|
LW_OPTION_EVENTSET_WAIT_CLR_ALL | 指定事件都为 0 时激活 |
LW_OPTION_EVENTSET_WAIT_CLR_ANY | 指定事件中任何一位为 0 时激活 |
LW_OPTION_EVENTSET_WAIT_SET_ALL | 指定事件都为 1 时激活 |
LW_OPTION_EVENTSET_WAIT_SET_ANY | 指定事件中任何一位为 1 时激活 |
LW_OPTION_EVENTSET_RETURN_ALL | 获得事件后返回所有有效的事件 |
LW_OPTION_EVENTSET_RESET | 获得事件后自动清除事件 |
LW_OPTION_EVENTSET_RESET_ALL | 获得事件后清除所有事件 |
事件集状态
#include <SylixOS.h>
ULONG Lw_Event_Status(LW_HANDLE ulId,
ULONG *pulEvent,
ULONG *pulOption);
函数 Lw_Event_Status 原型分析:
- 此函数成功时返回 ERROR_NONE,失败时返回错误号。
- 参数 ulId 是事件集的句柄。
- 输出参数 pulEvent 标识了当前置位的事件。
- 输出参数 pulOption 是事件集的创建选项。
事件集名字
#include <SylixOS.h>
ULONG Lw_Event_GetName(LW_HANDLE ulId,
PCHAR pcName);
函数 Lw_Event_GetName 原型分析:
- 此函数成功时返回 ERROR_NONE,失败时返回错误号。
- 参数 ulId 是事件集的句柄。
- 输出参数 pcName 是事件集的名字,pcName 应该指向一个大小为 LW_CFG_OBJECT_NAME_SIZE 的字符数组。
下面程序展示了 SylixOS 事件集的使用,程序创建两个线程和一个 SylixOS 事件集;线程 tTestB 不断地发送 0 到 31 号事件,线程 tTestA 等待事件并打印事件编号。
#include <SylixOS.h>
static LW_HANDLE _G_hEventSet;
static PVOID tTestA (PVOID pvArg)
{
INT iError;
ULONG ulEvent;
INT i;
while (1) {
iError = Lw_Event_WaitEx(_G_hEventSet,
LW_OPTION_EVENT_ALL,
LW_OPTION_EVENTSET_WAIT_SET_ANY |
LW_OPTION_EVENTSET_RESET,
LW_OPTION_WAIT_INFINITE,
&ulEvent);
if (iError != ERROR_NONE) {
break;
}
for (i = 0; i < 32; i++) {
if (ulEvent & (1 << i)) {
printf("tTestA(): get event %d\n", i);
}
}
}
return (LW_NULL);
}
static PVOID tTestB (PVOID pvArg)
{
INT iError;
INT i;
while (1) {
for (i = 0; i < 32; i++) {
iError = Lw_Event_Send(_G_hEventSet, (1 << i), LW_OPTION_EVENTSET_SET);
if (iError != ERROR_NONE) {
return (LW_NULL);
}
Lw_Time_SSleep(1);
}
}
return (LW_NULL);
}
int main (int argc, char *argv[])
{
LW_HANDLE hThreadAId;
LW_HANDLE hThreadBId;
_G_hEventSet = Lw_Event_Create("event_set", 0,
LW_OPTION_WAIT_FIFO |
LW_OPTION_OBJECT_LOCAL,
LW_NULL);
if (_G_hEventSet == LW_OBJECT_HANDLE_INVALID) {
printf("event set create failed.\n");
return (-1);
}
hThreadAId = Lw_Thread_Create("t_testa", tTestA, LW_NULL, LW_NULL);
if (hThreadAId == LW_OBJECT_HANDLE_INVALID) {
printf("t_testa create failed.\n");
return (-1);
}
hThreadBId = Lw_Thread_Create("t_testb", tTestB, LW_NULL, LW_NULL);
if (hThreadBId == LW_OBJECT_HANDLE_INVALID) {
printf("t_testb create failed.\n");
return (-1);
}
Lw_Thread_Join(hThreadAId, LW_NULL);
Lw_Thread_Join(hThreadBId, LW_NULL);
Lw_Event_Delete(&_G_hEventSet);
return (0);
}
在 SylixOS Shell 下运行程序:
# ./SylixOS_Event_Set
tTestA(): get event 0
tTestA(): get event 1
tTestA(): get event 2
tTestA(): get event 3
tTestA(): get event 4
tTestA(): get event 5
tTestA(): get event 6
......
# ts
NAME TID PID PRI STAT LOCK SAFE DELAY PAGEFAILS FPU CPU
------------------ ------- ----- --- ---- ---- ---- ---------- --------- --- ---
SylixOS_Event_Set 4010066 32 200 JOIN 0 0 1 USE 0
t_testa 4010067 32 200 ENTS 0 0 0 0
t_testb 4010068 32 200 SLP 0 193 0 0