【问题标题】:C++ ThreadSafe time counting classC++ 线程安全计时类
【发布时间】: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&lt;uint64_t&gt; 中不是更简单吗?那么就不需要复杂的线程同步了。
  • 在我的一生中,我无法理解您为什么将时间戳保留为无类型整数。整个 chrono 的存在就是为了避免这种恐惧!
  • 另外,你为什么放弃 std::chrono 的类型安全并回到 C 风格的编程?

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


【解决方案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;
}

【讨论】:

  • 是的,但实际上我想要实现的是将所有这些东西包装在一个类中。
  • @caffeine 然后把它包起来。但是扔掉所有线程安全的废话。
猜你喜欢
  • 2021-09-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-06-06
  • 1970-01-01
相关资源
最近更新 更多