【问题标题】:A thread-safe vector and string container?一个线程安全的向量和字符串容器?
【发布时间】:2010-08-25 16:48:13
【问题描述】:

我发布了一个先前的问题"Seg Fault when using std::string on an embedded Linux platform",在那里我得到了一些非常有用的建议。从那以后,我一直在从事其他项目,最近又开始研究这个问题。

重申一下,我仅限于使用 arm-linux 交叉编译器(版本 2.95.2),因为这是嵌入式平台供应商提供和支持的。我知道这个问题很可能是因为 stdlib 很旧,而且不是特别线程安全的。

问题是,每当我在多个线程中使用 STL 容器时,都会出现分段错误。除非我在容器声明周围使用 pthread_mutex_lock 和范围运算符(如其他帖子中所述),否则下面的代码将始终出现段错误。

在我的应用程序中使用这种方法是不可行的,因为我将容器传递给不同的方法和类。理想情况下,我想解决这个问题,或者找到一个合适的替代方案。我已经尝试过 STLPort 和 SGI 的标准模板库,结果相同。我只能假设因为它们是由非常旧的 gcc 链接的,所以它们无法解决问题。

有没有人有任何可能的建议或解决方案?或者您可以建议一个向量(和字符串)的实现,我可以将其放入我的代码中?

提前感谢您的任何指导。

#include <stdio.h>
  #include <vector>
  #include <list>
  #include <string>

  using namespace std;
    /////////////////////////////////////////////////////////////////////////////

    class TestSeg
    {
     static pthread_mutex_t     _logLock;
     public:
      TestSeg()
      {
      }

      ~TestSeg()
      {
      }

      static void* TestThread( void *arg )
      {
       int i = 0;
       while ( i++ < 10000 )
       {
        printf( "%d\n", i );
        WriteBad( "Function" );
       }
       pthread_exit( NULL );
      }

      static void WriteBad( const char* sFunction )
      {
       //pthread_mutex_lock( &_logLock );
       //{

       printf( "%s\n", sFunction );
       string sKiller;     //       <----------------------------------Bad
       //list<char> killer;    //       <----------------------------------Bad
       //vector<char> killer;    //       <----------------------------------Bad

       //}
       //pthread_mutex_unlock( &_logLock );

       return;
      }

      void RunTest()
      {
       int threads = 100;
       pthread_t     _rx_thread[threads];
       for ( int i = 0 ; i < threads ; i++ )
       {
        pthread_create( &_rx_thread[i], NULL, TestThread, NULL );
       }

       for ( int i = 0 ; i < threads ; i++ )
       {
        pthread_join( _rx_thread[i], NULL );
       }
      }

    };

    pthread_mutex_t       TestSeg::_logLock = PTHREAD_MUTEX_INITIALIZER;

    int main( int argc, char *argv[] )
    {
     TestSeg seg;
     seg.RunTest();
     pthread_exit( NULL );
    }

【问题讨论】:

  • 我很困惑——上面容器的使用完全基于堆栈(局部变量),因此容器本身永远不会跨线程访问。但是,您仍然会崩溃吗?这意味着仅仅在堆栈上创建 STL 容器就需要同步?我从来没有听说过这样的事情......
  • 迈克,这正是我的想法,以及为什么这让我如此头疼。我只能假设在实现中使用了一些静态变量来进行一些优化,而这些正是使其不安全的原因。

标签: c++ linux stl arm segmentation-fault


【解决方案1】:

问题不在于容器,而在于您的代码。

完全没有必要让容器本身成为线程安全的,因为您首先需要的是类似事务的语义。

为了演示,假设您有一个vector 的线程安全实现,例如。

  • 线程 1:if (!vec.empty())
  • 线程 2:vec.clear();
  • 线程 1:foo = vec.front();

这会导致未定义的行为。

问题在于,在容器线程安全上进行每个操作几乎毫无意义,因为您仍然需要能够连续锁定容器本身以执行多个操作。因此,您会锁定您的各种操作,然后再次锁定每个操作?

正如我所说:完全没有必要。

【讨论】:

  • Matthieu,我完全理解您所说的,并且理解在线程之间共享容器实例时会有未定义的行为。但是我遇到的问题是本地堆栈实例正在互相踩踏。为什么我需要锁定一个局部变量?
  • 这可能是因为您的内存分配方案不是线程安全的。默认情况下,您的编译器可能使用非线程安全的malloc/new,并且如果您通过正确的选项,它将进入线程安全版本......但现在这是一个牵强的猜测。至于static变量:STL代码可读性很强,你应该可以浏览实现(例如vector)。
【解决方案2】:

another thread 可能会回答您的部分问题。 C++的设计,包括标准库,受到很多因素的影响。效率是一个重复的主题。线程安全机制通常与效率目标不一致。图书馆的年代并不是真正的问题。

根据您的情况,您可以将 STL 向量包装在您自己的向量类中(您可以考虑使用Decorator),该类包含锁定机制并提供围绕访问的锁定/解锁逻辑。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多