可等待数值类

ColdMooon 2014-10-08 01:54:50
需求:
一个全局变量a.
一个线程可改变a.
多个线程在循环中检查a,如果跟上次看到的不一样,就执行f(),否则等待直到a改变.

看起来很简单,
版本1

int a;
for (;;)
{
int olda=0;
while(a==olda)
Sleep(1);
//时隙1
olda=a;
f();
}

有问题.
while(a==olda)这句,a可能被读到寄存器里,在接下来的循环中一直用寄存器里的值比较.将a声明为volatile可解决.
在时隙1,a可能被改变.导致此线程感觉不到a的下一次变化.

修改后:
版本2

volatile int a;
for (;;)
{
int olda=0;
int newa;
while((newa=a)==loda)
Sleep(1);
//时隙1
olda=newa;
f();
}


此段代码可以满足要求,
但忙询岂是我等追求效率的C++程序员所为!

版本3

template <class T>
class Atom
{
T tValue;
HANDLE hEvent;

public:
Atom()
{
hEvent = CreateEvent(0, true, false, 0); // 手动重置, 初始无信号
}

~Atom()
{
CloseHandle(hEvent);
}

void Set(T newValue)
{
tValue = newValue;
//时隙2
PulseEvent(hEvent);
}

BOOL Wait(T oldValue, DWORD ms)
{
if (tValue != oldValue)
return true;
else
//时隙3
return WaitForSingleObject(hEvent, ms) == WAIT_OBJECT_0;
}

void operator =(T newValue)
{
if (tValue != newValue)
Set(newValue);
}

operator T()
{
return tValue;
}
};

用等待解决效率问题,
但是时隙2中,等待的线程看到了值已改变执行了一次f()回头又等到了事件又执行一次f().
在时隙3中,可能错过了pulseevent.

看似简单的问题折腾我好久了.现在用版本2将就一下.期待更好的办法.
...全文
124 12 打赏 收藏 转发到动态 举报
写回复
用AI写文章
12 条回复
切换为时间正序
请发表友善的回复…
发表回复
ColdMooon 2014-10-09
  • 打赏
  • 举报
回复
这事光靠一个event是不行的. unix下有pthread_cond_t vista有SleepConditionVariable nt有signalobjectandwait 都可以达到要求. 但这是wince,只有互斥.信号,事件,简单的等待. 能不能用两个handle达到条件变量的效果?
schlafenhamster 2014-10-08
  • 打赏
  • 举报
回复
'修改的a的时候' 进入临界区
  • 打赏
  • 举报
回复
修改的a的时候判断值是否一样,不一样才通知另一个线程。另一个线程只要收到事件就执行f(),没有必要再去判断。
SiGoYi 2014-10-08
  • 打赏
  • 举报
回复
我明白了,楼主这个问题是由于线程中缺少消息循环造成的。 其实不用描述这么复杂,我以前遇到过,举例:线程A连续发送100个Event,线程B可能只等到70。 不加消息循环就会出现这样的情况。加个消息循环就好了。
SiGoYi 2014-10-08
  • 打赏
  • 举报
回复
引用 6 楼 ColdMooon 的回复:
临界区跟mutex没啥不一样的. 现在的问题是检查变量和等待事件之间,被pulseevent抢占了,就会漏掉这次事件.
意思是说在B等待之前,A修改了a并且发送了Event,这个时机B等不到这个Event,是这样么?
SiGoYi 2014-10-08
  • 打赏
  • 举报
回复
我怎么还是没太明白楼主呢!我来描述一下问题,楼主看看我描述得对不对。 假设:1、修改a的线程为A;2、监视a的线程为B。 线程A的处理是:1、修改a;2、发送Event; 线程B的处理是:1、判断a和原来值是否相等;2、WaitEvent;3、等到Event后执行f()。 是这样吧! 如果是按以上的描述,那么先把B启动,B判断完成后处于WaitEvent的状态。那么之后A修改了a,再发送Event,此时B是不会有问题的啊,因为此时B已经不判断a值了,即使是B循环回来再判断a值,那也没有问题啊!此时a的值不管有没有被A修改,B不还是要等待Event么! 我真没明白楼主说的时隙是什么意思。 另外想问一下楼主测试时出现问题了么?
ColdMooon 2014-10-08
  • 打赏
  • 举报
回复
临界区跟mutex没啥不一样的. 现在的问题是检查变量和等待事件之间,被pulseevent抢占了,就会漏掉这次事件.
schlafenhamster 2014-10-08
  • 打赏
  • 举报
回复
考虑 使用 临界区 ?
ColdMooon 2014-10-08
  • 打赏
  • 举报
回复
可能我说的不清楚. 我需要的是一个函数,wait(x). 如果a==x就等待,否则立即返回. 至于a被改变了几次不需要知道. 问题是修改a和setevent之间有一个时隙,如果被等待线程抢占就会让f多执行一次. 这个还好说,加个锁就行. 检查a和waitforsingleevent之间也有个时隙,这个没法加锁的. 虽说这个时隙被抢占的机会很少,总觉得不放心. 不行就用土办法,在检查a之前先sleep一下,睡一觉刚醒来精神好,就不会在关键时候掉链子.
SiGoYi 2014-10-08
  • 打赏
  • 举报
回复
有点搞不明白楼主了,再出一招。 你把修改a变量的线程做一下处理,就是每当a值被修改时,你就把修改的值存入一个队列中。之后给其他线程发送一个event,当其他线程收到这个event时,去检查这个队列,这样就知道a被修改几次了,之后最后一个线程用于清这个队列,就是说当所有的线程都处理完成后,最后一个执行的线程负责把这个队列清了。 注:修改a变量的线程往队列中存值时要做锁处理操作,最后一个线程在清队列时也做锁处理操作。 这样能解决你的问题。
ColdMooon 2014-10-08
  • 打赏
  • 举报
回复
找到一个办法: Thread1: WaitForSingleObject(hMutex); If (condition is false){ SignalObjectAndWait(hMutex, hEvent); WaitForSingleObject(hMutex); } Dosomething(); ReleaseMutex(hMutex); Thead2: WaitForSingleObject(hMutex); PulseEvent (hEvent); ReleaseMutex(hMutex); 但是我要在WinCE上使用,没有SignalObjectAndWait怎么办! 用ReleaseMutex+WaitForSingleObject(hEvent)这中间又产生一个时隙会出问题的! 观察者模式我考虑过,但是等待线程是不断加入和退出的. 加入观察者的代码也有同步问题,不好解决啊.
SiGoYi 2014-10-08
  • 打赏
  • 举报
回复
这不就类似观察者模式么,多个线程不需要检查a的值是否变化,可以把多个执行f()函数的线程注册到修改a的线程中,当修改a的线程发现a有变化时,再回调给其他多个线程不就行了么。

15,471

社区成员

发帖
与我相关
我的任务
社区描述
VC/MFC 进程/线程/DLL
社区管理员
  • 进程/线程/DLL社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧