概念
放锁与阻塞
有十个线程(thread0~9)和一个变量 V,thread0 需要写变量 V,thread1 ~ 9 需要读变量 V。我们假设只有在变量 V 的值改变时,读线程才需要读变量 V,在变量 V的值不变时,读者线程需要阻塞。
读线程阻塞前可能需要一种“判断”操作,判断变量 V 当前的值是否与上一次读到的值不一致;“判断”操作前需要加锁,如果一致那么读线程需要阻塞,阻塞前又需要释放该锁,释放锁与阻塞需要是一个不可打断的原子操作。我们可以想象一下,如果释放锁与阻塞不是一个原子操作,如果读线程在释放锁与阻 塞之间被thread0 抢占了,thread0可以成功获锁并改写变量 V,同时在thread0要以广播的方式通知多个读线程去读该变量。但变量 V 的值改变了读线程却阻塞了,显然读线程丢失了一次对变量 V 值改变的响应!
我们需要一种新的线程间通信手段来解决以上问题:释放锁与阻塞是一个原子操作和能以广播的方式通知多个读者线程。
线程条件变量
条件变量是多线程间的一种同步机制,能很好的解决以上问题。条件变量与互斥锁一起使用时,允许线程以无竞争的形式等待条件的发生。条件本身由互斥量保护,因此线程在改变条件之前必须首先锁住互斥量,其他线程在获得互斥量之前不会察觉到条件的改变。
SylixOS 条件变量的类型为 LW_THREAD_COND,使用时需要定义一个 该类型的变量。一个 SylixOS 条件变量必须要调用 Lw_Thread_Cond_Init 函数初始化之后才能使用。如果需要等待一个条件变量,可以调用 Lw_Thread_Cond_Wait 函数,中断服务程序不能调用 Lw_Thread_Cond_Wait 函数等待一个条件变量,因为 Lw_Thread_Cond_Wait 函数会阻塞当前线程。发送一个条件变量可以使用 Lw_Thread_Cond_Signal 或 Lw_Thread_Cond_Broadcast 函 数,中断服务程序也可以发送一个条件变量。当一个条件变量使用完毕后,应该调用 Lw_Thread_Cond_Destroy 函数删除,SylixOS会回收该条件变量占用的内核资源。需要注意的是,尝试再次使用一个已被删除的条件变量将会产生未知错误。
创建一个 SylixOS 条件变量需要使用 SylixOS 条件变量属性块。SylixOS 条件变量属性块的类型为 ULONG。属性有两种共享(LW_THREAD_PROCESS_SHARED)和私有(LW_THREAD_PROCESS_PRIVATE)。
接口
条件变量属性块操作接口
#define LW_THREAD_PROCESS_SHARED 0x1
#define LW_THREAD_PROCESS_PRIVATE 0x0
ULONG API_ThreadCondAttrInit (ULONG *pulAttr)
ULONG API_ThreadCondAttrDestroy (ULONG *pulAttr)
ULONG API_ThreadCondAttrSetPshared (ULONG *pulAttr, INT iShared)
ULONG API_ThreadCondAttrGetPshared (const ULONG *pulAttr, INT *piShared)
条件变量操作接口
ULONG API_ThreadCondInit (PLW_THREAD_COND ptcd, ULONG ulAttr)
ULONG API_ThreadCondDestroy (PLW_THREAD_COND ptcd)
ULONG API_ThreadCondSignal (PLW_THREAD_COND ptcd)
LW_API
ULONG API_ThreadCondBroadcast (PLW_THREAD_COND ptcd)
ULONG API_ThreadCondWait (PLW_THREAD_COND ptcd, LW_OBJECT_HANDLE ulMutex, ULONG ulTimeout)
用法
#include <stdio.h>
#include <SylixOS.h>
static INT _G_iCount = 0;
static LW_HANDLE _G_hLock;
static LW_THREAD_COND _G_threadCond;
static PVOID tTestR (PVOID pvArg)
{
INT iError;
while (1) {
iError = Lw_SemaphoreM_Wait(_G_hLock, LW_OPTION_WAIT_INFINITE);
if (iError != ERROR_NONE) {
break;
}
iError = Lw_Thread_Cond_Wait(&_G_threadCond, _G_hLock, LW_OPTION_WAIT_INFINITE);
if (iError != ERROR_NONE) {
Lw_SemaphoreM_Post(_G_hLock);
break;
}
printf("tTestR(%d): count = %d\n", (INT)pvArg, _G_iCount);
Lw_SemaphoreM_Post(_G_hLock);
}
return (LW_NULL);
}
static PVOID tTestW (PVOID pvArg)
{
INT iError;
while (1) {
iError = Lw_SemaphoreM_Wait(_G_hLock, LW_OPTION_WAIT_INFINITE);
if (iError != ERROR_NONE) {
break;
}
_G_iCount++;
printf("tTestW(): count = %d\n", _G_iCount);
Lw_Thread_Cond_Broadcast(&_G_threadCond);
Lw_SemaphoreM_Post(_G_hLock);
Lw_Time_SSleep(1);
if (_G_iCount == 3) {
break;
}
}
return (LW_NULL);
}
int main (int argc, char *argv[])
{
LW_CLASS_THREADATTR hThreadAttr = API_ThreadAttrGetDefault();
LW_HANDLE hThreadRId[3];
LW_HANDLE hThreadWId;
ULONG ulCondAttr;
INT iError;
INT i;
Lw_Thread_Condattr_Init(&ulCondAttr);
Lw_Thread_Condattr_Setpshared(&ulCondAttr, LW_FALSE);
iError = Lw_Thread_Cond_Init(&_G_threadCond, ulCondAttr);
if (iError != ERROR_NONE) {
printf("cond create failed.\n");
return (-1);
}
Lw_Thread_Condattr_Destroy(&ulCondAttr);
_G_hLock = Lw_SemaphoreM_Create("count_lock",
LW_PRIO_HIGH,
LW_OPTION_WAIT_FIFO |
LW_OPTION_OBJECT_LOCAL|
LW_OPTION_INHERIT_PRIORITY |
LW_OPTION_ERRORCHECK,
LW_NULL);
if (_G_hLock == LW_OBJECT_HANDLE_INVALID) {
printf("mutex create failed.\n");
return (-1);
}
for (i = 0; i < 3; i++) {
hThreadAttr.THREADATTR_pvArg = (PVOID)i;
hThreadRId[i] = Lw_Thread_Create("t_testr", tTestR, &hThreadAttr, LW_NULL);
if (hThreadRId[i] == LW_OBJECT_HANDLE_INVALID) {
printf("t_testr create failed.\n");
return (-1);
}
}
hThreadWId = Lw_Thread_Create("t_testw", tTestW, LW_NULL, LW_NULL);
if (hThreadWId == LW_OBJECT_HANDLE_INVALID) {
printf("t_testw create failed.\n");
return (-1);
}
Lw_Thread_Join(hThreadWId, LW_NULL);
Lw_Thread_Cond_Destroy(&_G_threadCond);
Lw_SemaphoreM_Delete(&_G_hLock);
return (0);
}
[root@sylixos:/root]
[root@sylixos:/root]
[root@sylixos:/root]
tTestW(): count = 1
tTestR(0): count = 1
tTestR(1): count = 1
tTestR(2): count = 1
tTestW(): count = 2
tTestR(0): count = 2
tTestR(1): count = 2
tTestR(2): count = 2
tTestW(): count = 3
tTestR(0): count = 3
tTestR(1): count = 3
tTestR(2): count = 3
[root@sylixos:/root]
|