【问题标题】:Why it's not thread safety and how to get it thread safety?为什么它不是线程安全以及如何获得线程安全?
【发布时间】:2014-03-10 23:53:54
【问题描述】:

FCGI 中有一些函数叫做“getRequestContent()”。它只是获取网络浏览器发布的数据。因此,我们的 c++ 应用程序类似于为 Web 客户端提供服务的守护进程。我认为我在非“线程安全”功能方面存在某种问题:

/**
 * Note this is not thread safe due to the static allocation of the
 * content_buffer.
 */
std::string getRequestContent(const FCGX_Request &request)
{
    char *content_length_str = FCGX_GetParam("CONTENT_LENGTH", request.envp);
    unsigned long content_length = STDIN_MAX;
    if (content_length_str)
    {
        content_length = strtol(content_length_str, &content_length_str, 10);
        if (*content_length_str)
        {
            std::cerr << "Can't Parse 'CONTENT_LENGTH='"
                      << FCGX_GetParam("CONTENT_LENGTH", request.envp)
                      << "'. Consuming stdin up to " << STDIN_MAX << "\n";
        }
        if (content_length > STDIN_MAX)
        {
            content_length = STDIN_MAX;
        }
    }
    else
    {
        content_length =
            0;  // Do not read from stdin if CONTENT_LENGTH is missing
    }
    char *content_buffer = new char[content_length];
    std::cin.read(content_buffer, content_length);
    content_length = std::cin.gcount();
    do
        std::cin.ignore(1024);
    while (std::cin.gcount() == 1024);
    std::string content(content_buffer, content_length);
    delete[] content_buffer;
    return content;
}

请解释一下为什么它不是线程安全代码?我们这里有什么样的问题?如何获得线程安全? :)

【问题讨论】:

    标签: c++ multithreading fastcgi


    【解决方案1】:

    主要问题是,正如评论所说,有一个静态分配的缓冲区。如果两个线程在该缓冲区上同步工作,您很可能会遇到竞争条件,因此您必须避免这种情况。

    这意味着要么修复FCGX_GetParam(我怀疑这是一个好主意,因为它是第三方库)或同步对它的访问:

    //some common mutex
    std::mutex mtx;
    
    std::string getRequestContent(const FCGX_Request &request)
    {
      std::string content_length_str;
      {
        lock(mtx); //guard every action on the static buffer with this lock
        char *content_length_cptr = FCGX_GetParam("CONTENT_LENGTH", request.envp);
        content_length_str = content_length_cptr; //copy the content of the buffer
      } //unlock the mutex, you dont work on the buffer hence forth  
    
      unsigned long content_length = 0;
      if (!content_length_str.empty()) try {
        content_length = boost::lexical_cast<unsigned long>(content_length_str);
        if (content_length > STDIN_MAX)
        {
          content_length = STDIN_MAX;
        }
      }
      catch(boost::bad_lexical_cast const&)
      {
        std::cerr << "Can't Parse 'CONTENT_LENGTH='"
                  << content_length_str
                  << "'. Consuming stdin up to " << STDIN_MAX << "\n";
        content_length = STDIN_MAX;
      }
    
      // the rest as it was...
    }
    

    【讨论】:

    • 感谢您的解决方案!但是正确声明的方式是:括号内的 std::string content_length_str 吗?在下一个括号“}”之后,我想我们无法访问 content_length_str 或者我错了?
    • 你是对的 - 它必须在外面声明。我修好了
    【解决方案2】:
    char * content_length_str = FCGX_GetParam( "CONTENT_LENGTH", request.envp );
    

    这条线可能意味着两件事:

    如果函数返回由 alloc/calloc 分配的块,要么你正在泄漏内存 或者该函数使用静态缓冲区。那将不是线程安全的。考虑到评论,我猜这是第二种选择。

    【讨论】:

    • @JavaRunner 你需要修复 FCGX_GetParam。
    • OMG :)) 我是否至少需要“删除 content_length_str;”在 getRequestContent 函数的末尾?
    • 我不能说,我不知道 FCGX_GetParam 在内部做什么。假设当前代码在单线程中运行时是正确的,那么删除内存可能会导致段错误。如果你愿意,你可以在一个额外的问题中发布它,包括 FCGX_GetParam 的代码。
    • 同步访问不安全资源就足够了。无需重写第三方库;)
    • @ArneMertz 是对的,如果对该函数的所有调用都在您的代码中并且您无法访问该函数的源代码,那么同步访问就足够了。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2010-12-10
    • 2012-11-20
    • 2013-10-11
    • 2020-10-10
    相关资源
    最近更新 更多