Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/multithreading/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 锁定std::list的有效方法_C++_Multithreading_List - Fatal编程技术网

C++ 锁定std::list的有效方法

C++ 锁定std::list的有效方法,c++,multithreading,list,C++,Multithreading,List,我知道std::list不是线程安全的。在我的应用程序中,线程不断向全局列表添加元素。另一个线程从列表中获取元素并逐个处理它们。但是,我不希望处理线程在处理过程中始终锁定列表 所以,处理线程会锁定列表,获取元素,解锁列表并处理元素。在处理过程中,其他线程不断向列表中添加元素。一旦处理结束,处理线程会再次锁定列表,删除已处理的元素并解除锁定 下面是伪代码: std::list<int> mylist ; /* Global list of integers */ void add_t

我知道std::list不是线程安全的。在我的应用程序中,线程不断向全局列表添加元素。另一个线程从列表中获取元素并逐个处理它们。但是,我不希望处理线程在处理过程中始终锁定列表

所以,处理线程会锁定列表,获取元素,解锁列表并处理元素。在处理过程中,其他线程不断向列表中添加元素。一旦处理结束,处理线程会再次锁定列表,删除已处理的元素并解除锁定

下面是伪代码:

std::list<int> mylist ; /* Global list of integers */

void add_thread(int element) /* Threads adding element to the list */
{
   write_lock();
   mylist.push_back(element);
   write_unlock();

   return;
}

void list_processing_thread() /* Processes elements from the list */
{
    for (std::list<int>::iterator it=mylist.begin(); it!=mylist.end(); ++it)
    {
        read_lock();
        int element = *it;
        read_unlock();

        process_element(element);

        write_lock();
        mylist.remove(element);
        write_unlock();
    }

    return;
}

以有效的方式处理列表元素是否正确?它会引起麻烦吗?

不,它不安全。是的,它会引起麻烦。 详细说明: 应该只有一个锁,即写锁。读者不需要互相防御。 在访问开始和结束迭代器之前,需要获得处理线程中的锁,因为它们可能会被写入线程损坏。获得锁后,复制列表,然后释放锁。完成处理后,获取锁并编辑原始列表。
祝你好运

首先,方法是好的,你应该锁定一小段时间,获取当前项目,解锁并处理该项目,同时列表被解锁并可供其他线程使用

但是,我建议您不要在列表未锁定时依赖列表迭代器。迭代器是狡猾的,当从迭代器中添加/删除项时,许多数据结构可能会损坏迭代器

我建议您使用以下方法:

while(list.empty() == false)
{
  lock();
  int element = list.front();
  list.pop_front();
  unlock();

  process(element);
}

最好的

在C++11中,有可能解决您的问题,但您可以轻松地将其移植到C++-03+boost或任何您拥有的线程库:

std::deque<int> queue;
std::mutex mtx;
std::condition_variable ready;

void producer()
{
  while (true)
  {
    int element = produce();

    std::unique_lock<std::mutex> lock(mtx);
    queue.push_back(element);
    lock.unlock(); 
    ready.notify_one();
  }
}

void consumer()
{
  while (true)
  {
    std::unique_lock<std::mutex> lock(mtx);
    ready.wait(lock, [](){ ! queue.empty() });
    int element = queue.front();
    queue.pop_front();
    lock.unlock();

    consume(element);
  }
}

通过不同的性能级别,您可以做很多事情,但有两件非常简单:

在处理线程中使用,将队列与空队列交换,这样,如果锁争用减慢了速度,那么至少您可以在一次命中中获得所有积压的项,从而将危害降至最低

使用单个写入程序/多个读卡器锁,例如,等

所以,处理线程会锁定列表,获取元素,解锁列表并处理元素。在处理过程中,其他线程不断向列表中添加元素。一旦处理结束,处理线程会再次锁定列表,删除已处理的元素并解除锁定

生产者线程彼此争用,消费者线程争用以访问列表

您可以通过为每个生产者提供自己的队列std::list+std::mutex或自旋锁来消除生产者之间的争用。这样:

生产者将项目发布到自己的队列中。创建一个元素的临时std::list,然后锁定互斥锁,将该元素拼接到队列中,解锁互斥锁。 当使用者准备就绪时,它会逐个锁定这些生产者队列,并将元素拼接到自己的队列中。拼接std::list是O1操作。 如果需要排序,则每个元素都应该有一个时间戳,以便使用者可以按时间对其合并队列中的所有元素进行排序。 上面的方法还使关键部分非常短,因为锁定互斥锁时所做的只是std::list拼接,这只是一些指针修改


或者,只需使用“英特尔线程构建块”中的类即可。

谢谢。但是list.empty即使在将元素添加到列表中时也能工作吗?或者还需要在那里锁定?是的,您需要锁定呼叫列表。空。此外,还需要一些条件变量来等待,如果列表为空,则生产者的速度慢于消费者的速度将导致消费者的旋转。当我们只想在处理成功时删除元素时,保持对list.empty的检查会变得有点棘手:并且我们不能像您在回答中提到的那样使用迭代器。读取锁定,因为线程同时读取时,writer可以等待…?是的Andrew,当编写器正在编写时,读者不需要阅读,因此这里正确的锁可能是读/写锁,它允许多个读者,但在编写器具有独占写锁时将其阻止。在这种情况下,似乎有多个编写器和一个读者;您的场景是多个生产者,一个消费者,对吗?@Maxim是的,这是正确的。这是一个有点幼稚的实现。queue.push_backelement;在锁定互斥锁时分配内存-可扩展性差。这就是为什么std::list在这种情况下更好,因为您可以分配一个新元素,然后锁定互斥锁并将其拼接到目标列表中,从而使您的关键部分真正快速-只更新4个指针。