C ++ List pop和push data race

时间:2014-01-09 07:27:42

标签: c++ multithreading std

对于std :: list l, 在thread1中,我做到了     l.push_back 在thread2中,我做到了     while(l.size()> 1)l.pop_front() 当这两个线程同时运行时,我得到了数据竞争。

让我感到困惑的是,我在pop_front之前检查过l.size是否大于1, 所以,当push_back时,没有任何情况下被推回的对象会有一个Null的前身, 然后我无法理解为什么会有数据竞争。

以下是我的测试中的代码:

#include <windows.h>
#include <list>

using namespace std;

HANDLE gsem = CreateSemaphore(NULL, 2, 2, NULL);
unsigned long __stdcall threadPoc(list<int>* l);
unsigned long __stdcall threadPoc2(list<int>* l);
int main()
{
  std::list<int> l;
  unsigned long a1, a2;
  HANDLE t[2];
  t[0] = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)threadPoc, &l, 0, &a1);
  t[1] = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)threadPoc2, &l, 0, &a2);

  ReleaseSemaphore(gsem, 2, NULL);

  WaitForMultipleObjects(2, t, TRUE, INFINITE);

  return 0;
}
bool exf = false;
unsigned long __stdcall threadPoc(list<int>* l)
{
  WaitForSingleObject(gsem, INFINITE);
  for (int i=0; i<100000; i++)
    l->push_back(i);
  exf = true;
  return 0;
}

unsigned long __stdcall threadPoc2(list<int>* l)
{
  WaitForSingleObject(gsem, INFINITE);
  while (l->size()>1 || !exf)
    l->pop_front();
  return 0;
}

2 个答案:

答案 0 :(得分:0)

在列表背面添加元素时,通常需要执行以下操作:

  1. 取列表的最后一个元素
  2. 让列表中的最后一个元素指向新元素
  3. 让新元素向后指向最后一个元素
  4. 让列表指向新的最后一个元素
  5. 如果您的列表包含1个元素,而另一个线程在步骤1和2之间删除了该元素,则您的列表将会损坏。

    使用互斥锁(CriticalSection)保护列表,或使用无锁列表(例如Microsoft的PPL库中的concurrent_queue,请参阅http://msdn.microsoft.com/en-us/library/dd504906.aspx#queue)。

答案 1 :(得分:0)

并发是一个很大的话题,首先我建议你阅读Anthony Williams的“C ++ Concurrency in Action”,你会在阅读完这本书后了解几乎所有内容。

首先,记住一件事,一个线程写入时总会发生冲突,另一个线程读/写同一个对象。

其次,对于你的具体问题,显然有几个冲突的地方。

  1. l.push_back很可能(好吧,取决于列表实现)改变大小,l.size()是一个读取,所以它是在相同变量大小上的写 - 读冲突。并且l.pop_front也很可能会改变大小,因此它是写 - 写冲突。考虑什么时候push_back和amp; pop_front更改大小,大小将会损坏。

  2. 似乎l.push_back&amp; l.pop_front没有写入 - 写入冲突,因为C ++的列表是一个双向链表,而push_back / pop_front没有处理同一个变量,但你可能错了。一个显而易见的事情是,push_back()可能涉及内存分配,pop_front()可能涉及内存释放。内存填充由template-id Alloc处理。您很有可能内存分配/释放存在冲突。这完全取决于实现,其中不同的库可能有不同的实现。