c – 在实时应用程序中多个线程之间同步容器访问的最佳方式是什么?

我有std :: list< Info>我的应用程序中的infoList在两个线程之间共享.这两个线程正在访问此列表,如下所示:

线程1:在列表上使用push_back(),pop_front()或clear()(视情况而定)
线程2:使用迭代器遍历列表中的项目并执行一些操作.

线程2正在迭代列表,如下所示:

for(std::list<Info>::iterator i = infoList.begin(); i != infoList.end(); ++i)
{
  DoAction(i);
}

代码是使用GCC 4.4.2编译的.

有时我会导致segfault并且崩溃应用程序.错误是在std_list.h第143行引起的,如下所示:

_M_node = _M_node->_M_next;

我猜这是一个赛车条件.线程2正在迭代时,该列表可能已更改或甚至被线程1清除.

我使用Mutex来同步访问这个列表,并且在我的初始测试期间,所有的都进行了.但系统在压力测试下冻结,使得该解决方案完全不可接受.该应用程序是一个实时应用程序,我需要找到一个解决方案,这样两个线程可以尽可能快地运行,而不会影响总应用程序吞吐量.

我的问题是这样的:
线程1和线程2需要尽可能快地执行,因为这是一个实时应用程序.我可以做什么来防止这个问题,仍然保持应用程序的性能?有没有免费的算法可用于这样的问题?

如果我在线程2的迭代中错过了一些新添加的Info对象,那么它可以,但是我可以做什么来防止迭代器成为悬挂指针?

谢谢

解决方法

一般来说,以这种方式使用STL容器是不安全的.您将必须实现特定的方法,使您的代码线程安全.您选择的解决方案取决于您的需求.我可能通过维护两个列表来解决这个问题,每个列表中有一个.并通过 lock free queue(在评论中提到这个问题)传达变化.您也可以通过将它们包装在boost :: shared_ptr中来限制Info对象的生命周期.
typedef boost::shared_ptr<Info> InfoReference; 
typedef std::list<InfoReference> InfoList;

enum CommandValue
{
    Insert,Delete
}

struct Command
{
    CommandValue operation;
    InfoReference reference;
}

typedef LockFreeQueue<Command> CommandQueue;

class Thread1
{
    Thread1(CommandQueue queue) : m_commands(queue) {}
    void run()
    {
        while (!finished)
        {
            //Process Items and use 
            // deleteInfo() or addInfo()
        };

    }

    void deleteInfo(InfoReference reference)
    {
        Command command;
        command.operation = Delete;
        command.reference = reference;
        m_commands.produce(command);
    }

    void addInfo(InfoReference reference)
    {
        Command command;
        command.operation = Insert;
        command.reference = reference;
        m_commands.produce(command);
    }
}

private:
    CommandQueue& m_commands;
    InfoList m_infoList;
}   

class Thread2
{
    Thread2(CommandQueue queue) : m_commands(queue) {}

    void run()
    {
        while(!finished)
        {
            processQueue();
            processList();
        }   
    }

    void processQueue()
    {
        Command command;
        while (m_commands.consume(command))
        {
            switch(command.operation)
            {
                case Insert:
                    m_infoList.push_back(command.reference);
                    break;
                case Delete:
                    m_infoList.remove(command.reference);
                    break;
            }
        }
    }

    void processList()
    {
        // Iterate over m_infoList
    }

private:
    CommandQueue& m_commands;
    InfoList m_infoList;
}   


void main()
{
CommandQueue commands;

Thread1 thread1(commands);
Thread2 thread2(commands);

thread1.start();
thread2.start();

waitforTermination();

}

这还没有编译.您仍然需要确保对Info对象的访问是线程安全的.

相关文章

本程序的编译和运行环境如下(如果有运行方面的问题欢迎在评...
水了一学期的院选修,万万没想到期末考试还有比较硬核的编程...
补充一下,先前文章末尾给出的下载链接的完整代码含有部分C&...
思路如标题所说采用模N取余法,难点是这个除法过程如何实现。...
本篇博客有更新!!!更新后效果图如下: 文章末尾的完整代码...
刚开始学习模块化程序设计时,估计大家都被形参和实参搞迷糊...