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

令我感到困惑的是,我在pop_front之前检查了l.size是否大于一个,
因此,在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;
}

最佳答案

并发性是一个大话题,首先,我请您阅读Anthony Williams的“C++并发性”,在读完本书之后,您几乎可以理解所有内容。

首先,请记住一件事,当一个线程被写入,而另一线程被读取/写入同一对象时,总是存在冲突。

其次,对于您的特定问题,显然有几个冲突的地方。

  • l.push_back最有可能(取决于列表实现)更改大小,l.size()是读取,因此在相同的变量大小上是写-读冲突。而且l.pop_front很有可能也会更改大小,因此这是一个写-写冲突。考虑一下,当push_back和pop_front都更改大小时,该大小将损坏。
  • 似乎l.push_back和l.pop_front没有写-写冲突,因为C++的列表是一个双链表,而push_back/pop_front并没有处理相同的变量,但是您可能错了。一件事很明显,push_back()可能涉及内存分配,pop_front()可能涉及内存释放。内存内容由模板ID Alloc处理。您很有可能出现内存分配/取消分配冲突的情况。这完全取决于实现,其中不同的库可能具有不同的实现。
  • 10-07 19:25
    查看更多