我试图通过跳过昂贵的事件操作来提高生产者/消费者线程的效率,如果有必要,可以使用以下内容:
//cas(variable, compare, set) is atomic compare and swap
//queue is already lock free
running = false
// dd item to queue – producer thread(s)
if(cas(running, false, true))
{
// We effectively obtained a lock on signalling the event
add_to_queue()
signal_event()
}
else
{
// Most of the time if things are busy we should not be signalling the event
add_to_queue()
if(cas(running, false, true))
signal_event()
}
...
// Process queue, single consumer thread
reset_event()
while(1)
{
wait_for_auto_reset_event() // Preferably IOCP
for(int i = 0; i < SpinCount; ++i)
process_queue()
cas(running, true, false)
if(queue_not_empty())
if(cas(running, false, true))
signal_event()
}
显然试图让这些事情变得正确有点棘手(!)上面的伪代码是否正确?一个解决方案可以向事件发出超过完全需要的信号,但不是每个项目都能这样做。
答案 0 :(得分:2)
这属于“停止搞乱并重新开始工作”的子类别,称为“过早优化”。 : - )
如果“昂贵的”事件操作占用了相当长的一部分时间,那么您的设计是错误的,而不是使用生产者/消费者,您应该使用临界区/互斥体,并从调用线程中完成工作。
如果您真的担心,我建议您分析您的申请。
<强>更新强>
正确答案:
生产者
ProducerAddToQueue(pQueue,pItem){
EnterCriticalSection(pQueue->pCritSec)
if(IsQueueEmpty(pQueue)){
SignalEvent(pQueue->hEvent)
}
AddToQueue(pQueue, pItem)
LeaveCriticalSection(pQueue->pCritSec)
}
消费
nCheckQuitInterval = 100; // Every 100 ms consumer checks if it should quit.
ConsumerRun(pQueue)
{
while(!ShouldQuit())
{
Item* pCurrentItem = NULL;
EnterCriticalSection(pQueue-pCritSec);
if(IsQueueEmpty(pQueue))
{
ResetEvent(pQueue->hEvent)
}
else
{
pCurrentItem = RemoveFromQueue(pQueue);
}
LeaveCriticalSection(pQueue->pCritSec);
if(pCurrentItem){
ProcessItem(pCurrentItem);
pCurrentItem = NULL;
}
else
{
// Wait for items to be added.
WaitForSingleObject(pQueue->hEvent, nCheckQuitInterval);
}
}
}
注意:
假设:
答案 1 :(得分:0)
CAS价格昂贵(不像信号那么贵)。如果您希望跳过信号是常见的,我会按如下方式编写CAS代码:
bool cas(variable, old_val, new_val) {
if (variable != old_val) return false
asm cmpxchg
}
像这样的无锁结构Jinx(我工作的产品)非常擅长测试。因此,您可能希望使用eval许可证来测试无锁队列和信号优化逻辑。
编辑:也许你可以简化这种逻辑。
running = false
// add item to queue – producer thread(s)
add_to_queue()
if (cas(running, false, true)) {
signal_event()
}
// Process queue, single consumer thread
reset_event()
while(1)
{
wait_for_auto_reset_event() // Preferably IOCP
for(int i = 0; i < SpinCount; ++i)
process_queue()
cas(running, true, false) // this could just be a memory barriered store of false
if(queue_not_empty())
if(cas(running, false, true))
signal_event()
}
现在cas / signal总是彼此相邻,它们可以移动到子程序中。
答案 2 :(得分:0)
为什么不将bool
与活动相关联?使用cas
将其设置为true,如果cas
成功,则发出事件信号,因为事件必须已清除。然后服务员可以在等待之前清除标志
bool flag=false;
// producer
add_to_queue();
if(cas(flag,false,true))
{
signal_event();
}
// consumer
while(true)
{
while(queue_not_empty())
{
process_queue();
}
cas(flag,true,false); // clear the flag
if(queue_is_empty())
wait_for_auto_reset_event();
}
这样,您只需等待队列中没有元素,并且您只为每批项目发出一次事件信号。
答案 3 :(得分:0)
我相信,你想要在这个问题上实现这样的目标:
WinForms Multithreading: Execute a GUI update only if the previous one has finished.它特定于C#和Winforms,但结构可能适用于您。