【问题标题】:Modifying member variable in one thread and reading in a different thread在一个线程中修改成员变量并在另一个线程中读取
【发布时间】:2018-09-07 09:39:57
【问题描述】:

我有两个成员变量。

class MessageQueues {
    ....
    char* m_InputBuffer;
    uint32_t m_InputBufferSize;
    ....
};

我在当前线程的函数中更新它们,甚至使用调试器查看成员变量已更新。但是在另一个函数中,在单独的线程中运行,成员变量包含垃圾。

所以,我有一个函数,它读取两个值并将它们写入两个成员变量,并在完成后触发另一个线程。

ErrorCode MessageQueue::handleIncomingMessage(char receiveBuffer[], const uint32_t bufferSize) {
    ES_TRC3("started");
    ErrorCode errorCode = ES_SUCCESS;
    pthread_mutex_lock(&mutexMsgQueueIncoming);
    m_InputBuffer = receiveBuffer;
    m_InputBufferSize = bufferSize;
    ES_TRC3("triggering and unlocking");
    pthread_cond_signal(&msgQueueCondition);
    pthread_mutex_unlock(&mutexMsgQueueIncoming);
    ES_TRC3("triggered and unlocked");
    return errorCode;
}

然后在第二个成员函数中,它无限期地在单独的线程中运行并等待消息,

ErrorCode MessageQueue::runReceiver(void) {
    ES_TRC3("started");
    ErrorCode errorCode = ES_SUCCESS;
    while(true)
    {
        ES_TRC3("waiting for input messages");
        pthread_cond_wait(&msgQueueCondition, &mutexMsgQueueIncoming);
        pthread_mutex_lock(&mutexMsgQueueIncoming);
        ES_TRC3("will parse message");
        if (strlen(m_InputBuffer) > 0UL) {
            if ((errorCode = m_MsgProtocol->parseMessage(m_InputBuffer, m_InputBufferSize)) != ES_SUCCESS)
            {
                ES_TRC1("failed to parseMessage, error:%d", errorCode);
            }
        }
        ES_TRC3("message parsed");
        pthread_mutex_unlock(&mutexMsgQueueIncoming);
    }
    return errorCode;
}

使用调试器(以及日志消息),我可以看到变量的更新和触发以正确的顺序发生。所以我不明白为什么在第二个函数中,成员变量似乎不包含正确的值。我在某处读到了一些关于“易失性”的东西。但不要真的认为,这是正确的答案。我在这里做错了吗?

更新: 互斥锁在类之外,但在同一个源文件中,在顶部初始化,如下所示。

pthread_mutex_t mutexMsgQueueIncoming       = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t msgQueueCondition            = PTHREAD_COND_INITIALIZER;

其实很奇怪。当我在没有调试器的情况下运行程序时,似乎无法在 runReceiver() 函数中锁定互斥锁。使用调试器,互斥锁被锁定,但成员变量包含垃圾。好的,我想我应该调查一下。

【问题讨论】:

  • 你在哪里锁定第一块代码?有解锁但没有锁定?
  • @JoeyMallone 是否有可能 mutexMsgQueueIncoming 在两个线程中不同?也许某些具有不同初始化互斥锁的类复制构造函数?您是否尝试过从两个线程通过其指针访问类实例,而不是通过值传递它?此外,如果在第二个街区出现虚假唤醒怎么办?它如何重新等待?
  • @huseyintugrulbuyukisik,好的,让我调查一下。感谢您的想法/提示。
  • @Joey 也许有一天!

标签: c++ pthreads


【解决方案1】:

这里有几个错误:

  1. 必须在互斥锁锁定时等待条件变量。 pthread_cond_wait 文档是必读的。
  2. 条件变量spurious wake-ups 必须由while 循环处理。

修复:

class MessageQueues {
    ....
    char* m_InputBuffer;
    uint32_t m_InputBufferSize;
    uint32_t m_WriteGeneration = 0; // <--- a fix.
    uint32_t m_ReadGeneration = 0;  // <--- a fix.
    ....
};

ErrorCode MessageQueue::handleIncomingMessage(char receiveBuffer[], const uint32_t bufferSize) {
    ES_TRC3("started");
    ErrorCode errorCode = ES_SUCCESS;
    pthread_mutex_lock(&mutexMsgQueueIncoming);
    m_InputBuffer = receiveBuffer;
    m_InputBufferSize = bufferSize;
    ++m_WriteGeneration; // <--- a fix.
    ES_TRC3("triggering and unlocking");
    pthread_cond_signal(&msgQueueCondition);
    pthread_mutex_unlock(&mutexMsgQueueIncoming);
    ES_TRC3("triggered and unlocked");
    return errorCode;
}

ErrorCode MessageQueue::runReceiver(void) {
    ES_TRC3("started");
    ErrorCode errorCode = ES_SUCCESS;
    while(true)
    {
        ES_TRC3("waiting for input messages");
        pthread_mutex_lock(&mutexMsgQueueIncoming);  // <--- a fix.
        while(m_ReadGeneration == m_WriteGeneration) // <--- a fix.
            pthread_cond_wait(&msgQueueCondition, &mutexMsgQueueIncoming); // <--- a fix.
        m_ReadGeneration = m_WriteGeneration; // <--- a fix.
        ES_TRC3("will parse message");
        if (strlen(m_InputBuffer) > 0UL) {
            if ((errorCode = m_MsgProtocol->parseMessage(m_InputBuffer, m_InputBufferSize)) != ES_SUCCESS)
            {
                ES_TRC1("failed to parseMessage, error:%d", errorCode);
            }
        }
        ES_TRC3("message parsed");
        pthread_mutex_unlock(&mutexMsgQueueIncoming);
    }
    return errorCode;
}

请注意,如果在runReceiver 解锁互斥锁时调用了两次handleIncomingMessage,则此代码将丢失消息。您可能需要一个消息队列,而不是存储最后一条消息(即,您有一个大小为 1 的队列,它会在溢出时丢弃旧元素)。

代码还需要检查 pthread 函数返回的错误代码。这相当乏味,最好使用 C++11 std::mutexstd::condition_variablestd::unique_lock 为您进行检查。在 C++98 中,您可以使用 boost 等价物。

【讨论】:

  • 哎呀谢谢。我误解了如何使用 pthread_cond_wait() 。让我午饭后测试一下。大概这就是错误。我应该更熟悉 pthread 库。谢谢。 p.s.我不能使用 C++11。我无法使用 Boost。我被困在 1998 年。:P
  • @JoeyMallone 在 1998 年你可以使用 Boost。
  • 天哪! :O 那我什至不在 1998 年!我无法使用 Boost。不是我的选择。这不是我的选择。
  • @JoeyMallone 在 pthread 原语上制作薄包装器,然后为您进行错误检查。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-12-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多