C ++ ThreadSafe计时类

时间:2018-12-28 14:54:31

标签: c++ multithreading time thread-safety mutex

我正在尝试构建一个简单的线程安全时间计数器类。我设法编写的代码如下:

#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()来尽可能简单地计算时间,但是我的计划是将来进一步开发此类。

后来编辑:我的问题不是我真的如何获得时间戳。我的问题是,鉴于Timer类的同一实例将在多个线程之间共享,我该如何读/写安全的_PreviousUs

1 个答案:

答案 0 :(得分:1)

您的课程看起来不正确。

有一个示例如何在std::chrono::duration_cast中测量时间:

#include <iostream>
#include <chrono>
#include <ratio>
#include <thread>

void f()
{
    std::this_thread::sleep_for(std::chrono::seconds(1));
}

int main()
{
    auto t1 = std::chrono::high_resolution_clock::now();
    f();
    auto t2 = std::chrono::high_resolution_clock::now();

    // floating-point duration: no duration_cast needed
    std::chrono::duration<double, std::milli> fp_ms = t2 - t1;

    // integral duration: requires duration_cast
    auto int_ms = std::chrono::duration_cast<std::chrono::milliseconds>(t2 - t1);

    // converting integral duration to integral duration of shorter divisible time unit:
    // no duration_cast needed
    std::chrono::duration<long, std::micro> int_usec = int_ms;

    std::cout << "f() took " << fp_ms.count() << " ms, "
              << "or " << int_ms.count() << " whole milliseconds "
              << "(which is " << int_usec.count() << " whole microseconds)" << std::endl;
}