3

我想在这条线上实现一些东西:

        inline void DecrementPendingWorkItems()
        {
            if(this->pendingWorkItems != 0) //make sure we don't underflow and get a very high number
            {
                ::InterlockedDecrement(&this->pendingWorkItems);
            }
        }

我怎样才能做到这一点,以便两个操作作为一个块是原子的,而不使用锁?

4

5 回答 5

2

最简单的解决方案是在整个部分周围使用互斥锁(以及对 的所有其他访问this->pendingWorkItems)。如果由于某种原因这是不可接受的,那么您可能需要比较和交换:

void decrementPendingWorkItems()
{
    int count = std::atomic_load( &pendingWorkItems );
    while ( count != 0
            && ! std::atomic_compare_exchange_weak( 
                    &pendingWorkItems, &count, count - 1 ) ) {
    }
}

(这假设pendingWorkItems具有类型std::atomic_int。)

于 2012-07-11T09:15:47.083 回答
2

您可以只检查结果,InterlockedDecrement()如果它恰好为负数(如果更可取,则为 <= 0)通过调用InterlockedIncrement(). 在其他适当的代码中应该没问题。

于 2012-07-11T08:54:23.533 回答
0

使用原子 CAS。 http://msdn.microsoft.com/en-us/library/windows/desktop/ms683560(v=vs.85).aspx

您可以使其无锁,但不能免费等待。

正如基里尔建议的那样,这类似于您的情况下的自旋锁。

我认为这可以满足您的需求,但我建议您在继续使用它之前考虑所有可能性,因为我根本没有测试过它:

inline bool
InterlockedSetIfEqual(volatile LONG* dest, LONG exchange, LONG comperand)
{
    return comperand == ::InterlockedCompareExchange(dest, exchange, comperand);
}

inline bool InterlockedDecrementNotZero(volatile LONG* ptr)
{
    LONG comperand;
    LONG exchange;
    do {
        comperand = *ptr;
        exchange = comperand-1;
        if (comperand <= 0) {
            return false;
        }
    } while (!InterlockedSetIfEqual(ptr,exchange,comperand));
    return true;
}

关于为什么您的待处理工作项应该低于零的问题仍然存在。你真的应该确保增量的数量与减量的数量相匹配,一切都会好起来的。如果违反了这个约束,我可能会添加一个断言或异常。

于 2012-07-11T09:12:38.443 回答
0

有一种叫做“SpinLock”的东西。这是一个非常轻量级的同步。

这是这样的想法:

//
//    This lock should be used only when operation with protected resource
//  is very short like several comparisons or assignments.
//
class SpinLock
{
 public:

      __forceinline SpinLock() { body = 0; }
      __forceinline void Lock()
           {
             int spin = 15;
             for(;;) {
               if(!InterlockedExchange(&body, 1)) break;
               if(--spin == 0) { Sleep(10); spin = 29; }
             }
           }

      __forceinline void Unlock() { InterlockedExchange(&body, 0); }

 protected:

    long   body;

};

样本中的实际数字并不重要。这种锁非常有效。

于 2012-07-11T08:45:56.227 回答
0

您可以InterlockedCompareExchange在循环中使用:

    inline void DecrementPendingWorkItems() {
        LONG old_items = this->pendingWorkingItems;
        LONG items;
        while ((items = old_items) > 0) {
            old_items = ::InterlockedCompareExchange(&this->pendingWorkItems,
                                                     items-1, items);
            if (old_items == items) break;
        }
    }

InterlockedCompareExchange函数的作用是:

  if pendingWorkItems matches items, then
    set the value to items-1 and return items
  else return pendingWorkItems

这是原子完成的,也称为比较和交换

于 2012-07-11T08:53:52.957 回答