【发布时间】:2018-12-28 14:54:31
【问题描述】:
我正在尝试构建一个简单的线程安全时间计数器类。我设法编写的代码如下:
#include <iostream>
#include <chrono>
#include <mutex>
#include <condition_variable>
/* Get timestamp in microseconds */
static inline uint64_t micros()
{
return (uint64_t)std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::high_resolution_clock::now().time_since_epoch()).count();
}
class Timer
{
public:
explicit Timer() = default;
/**
* @brief Restart the counter
*/
void Restart()
{
std::unique_lock<std::mutex> mlock(_mutex);
{
this->_PreviousUs = micros();
this->_IsRunning = true;
}
mlock.unlock();
_cond.notify_one();
}
/**
* @brief Stop the timer
*/
void Stop()
{
std::unique_lock<std::mutex> mlock(_mutex);
{
this->_IsRunning = false;
}
mlock.unlock();
_cond.notify_one();
}
/**
* @brief Check whether counter is started or not
* @return true if timer is running, false otherwise
*/
bool IsRunning()
{
std::unique_lock<std::mutex> mlock(_mutex);
bool tmp = _IsRunning;
mlock.unlock();
_cond.notify_one();
return tmp;
}
/**
* @brief Calculate number of elapsed milliseconds from current timestamp
* @return Return elapsed milliseconds
*/
uint64_t ElapsedMs()
{
std::unique_lock<std::mutex> mlock(_mutex);
uint64_t tmp = _PreviousUs;
mlock.unlock();
_cond.notify_one();
return ( millis() - (tmp/1000u) );
}
/**
* @brief Calculate number of elapsed microseconds from current timestamp
* @return Return elapsed microseconds
*/
uint64_t ElapsedUs()
{
std::unique_lock<std::mutex> mlock(_mutex);
uint64_t tmp = _PreviousUs;
mlock.unlock();
_cond.notify_one();
return ( micros() - tmp );
}
private:
/** Timer's state */
bool _IsRunning = false;
/** Thread sync for read/write */
std::mutex _mutex;
std::condition_variable _cond;
/** Remember when timer was stated */
uint64_t _PreviousUs = 0;
};
用法很简单。我只是创建一个全局变量,然后从几个不同的线程访问它。
/* global variable */
Timer timer;
..............................
/* restart in some methods */
timer.Restart();
...............................
/* From some other threads */
if(timer.IsRunning())
{
// retrieve time since Restsrt() then do something
timer.ElapsedMs();
// Restart eventually
timer.Restart();
}
它在 Linux 下工作,现在很好。但是让我担心的代码是这样的:
std::unique_lock<std::mutex> mlock(_mutex);
uint64_t tmp = _PreviousUs;
mlock.unlock();
_cond.notify_one();
return ( micros() - tmp );
为了“线程安全”,每次检查经过的时间时,我都必须创建一个临时变量。 有什么方法可以改进我的代码并同时保证它的线程安全?
PS:我知道我只能使用函数micros() 来尽可能简单地计算时间,但我计划在未来进一步开发这个类。
稍后编辑:我的问题并不是我如何获得时间戳。我的问题是我如何读/写安全的_PreviousUs,因为 Timer 类的同一个实例将在多个线程之间共享?
【问题讨论】:
-
将时间戳存储在
std::atomic<uint64_t>中不是更简单吗?那么就不需要复杂的线程同步了。 -
在我的一生中,我无法理解您为什么将时间戳保留为无类型整数。整个 chrono 的存在就是为了避免这种恐惧!
-
另外,你为什么放弃
std::chrono的类型安全并回到 C 风格的编程?
标签: c++ multithreading time thread-safety mutex