2014-10-27 71 views
0

我正在构建一个模拟器来测试一个非常简单的机器人的学生代码。我需要定期在不同的线程上运行两个功能(更新机器人传感器和机器人位置)。我目前的实现是高效率的处理器,因为它有一个线程专门用于简单地递增数字以跟踪代码中的位置。我最近的理论是,我可以使用睡眠来给出更新传感器值和机器人位置之间的时间延迟。我的第一个问题是:这是否有效?第二:有没有办法做简单的事情,但测量时钟周期而不是秒?使用线程定时调用函数

+0

你能详细解释一下你希望做什么吗?为什么传感器和位置有不同的线程?这两个线程是否以任何方式相互依赖?除非测试代码正在做一些非常繁重的工作,否则我怀疑你需要认真关注优化测试代码的效率。 – 2014-10-27 18:03:32

回答

1

通过等待类似互斥体的对象使线程进入睡眠通常是有效的。一种常见的模式包括等待一个超时的互斥体。达到超时时间后,时间间隔结束。当互斥体被释放时,它是线程终止的信号。

伪代码:

void threadMethod() { 
    for(;;) { 
    bool signalled = this->mutex.wait(1000); 
    if(signalled) { 
     break; // Signalled, owners wants us to terminate 
    } 

    // Timeout, meaning our wait time is up 
    doPeriodicAction(); 
    } 
} 

void start() { 
    this->mutex.enter(); 
    this->thread.start(threadMethod); 
} 

void stop() { 
    this->mutex.leave(); 
    this->thread.join(); 
} 

在Windows系统中,超时通常以毫秒为单位指定和精确到大约16内毫秒(timeBeginPeriod()可能能够改善这一点)。我不知道CPU周期触发的同步原语。在委派给OS线程调度器之前,有一些称为“关键部分”的轻量级互斥体,可让CPU旋转几千个周期。在这段时间内,它们相当准确。

在Linux系统上,准确度可能会高一些(高频定时器或无滴答内核),除了互斥锁之外,还有类似于Windows临界区的“futexes”(快速互斥锁)。


我不知道我抓住你想要达到什么样的,但如果你想测试学生的代码,你可能想要使用虚拟时钟和控制自己时间的流逝。例如,通过调用学生必须提供的processInputs()decideMovements()方法。每次通话后,1个时隙到了。

0

是的,你的理论是正确的。你可以使用睡眠来在线程执行一个函数之间放一些延迟。效率取决于您可以选择多大的延迟以获得期望的结果。 你必须解释你的实施细节。例如,我们不知道两个线程是否有依赖关系(在这种情况下,您必须注意可能会导致一些周期的同步)。

+0

你可以使用'sleep()',是的。但是在大多数情况下,这可能是一个非常糟糕的主意(你可能会忙于等待)。更合适的方法是使用定时信号/条件变量。 – 2014-10-27 18:22:24

1

这个C++ 11代码使用std::chrono::high_resolution_clock来测量亚秒的时间,而std::thread运行三个线程。 std::this_thread::sleep_for()函数用于在指定的时间内休眠。

#include <iostream> 
#include <thread> 
#include <vector> 
#include <chrono> 

void seconds() 
{ 
    using namespace std::chrono; 

    high_resolution_clock::time_point t1, t2; 
    for (unsigned i=0; i<10; ++i) { 
     std::cout << i << "\n"; 
     t1 = high_resolution_clock::now(); 
     std::this_thread::sleep_for(std::chrono::seconds(1)); 
     t2 = high_resolution_clock::now(); 
     duration<double> elapsed = duration_cast<duration<double> >(t2-t1); 
     std::cout << "\t(" << elapsed.count() << " seconds)\n"; 
    } 
} 
int main() 
{ 
    std::vector<std::thread> t; 

    t.push_back(std::thread{[](){ 
     std::this_thread::sleep_for(std::chrono::seconds(3)); 
     std::cout << "awoke after 3\n"; }}); 
    t.push_back(std::thread{[](){ 
     std::this_thread::sleep_for(std::chrono::seconds(7)); 
     std::cout << "awoke after 7\n"; }}); 
    t.push_back(std::thread{seconds}); 

    for (auto &thr : t) 
     thr.join(); 
} 

很难知道这是否符合您的需求,因为这个问题中缺少很多细节。在Linux下,编译:我的机器上

g++ -Wall -Wextra -pedantic -std=c++11 timers.cpp -o timers -lpthread 

输出:

0 
    (1.00014 seconds) 
1 
    (1.00014 seconds) 
2 
awoke after 3 
    (1.00009 seconds) 
3 
    (1.00015 seconds) 
4 
    (1.00011 seconds) 
5 
    (1.00013 seconds) 
6 
awoke after 7 
    (1.0001 seconds) 
7 
    (1.00015 seconds) 
8 
    (1.00014 seconds) 
9 
    (1.00013 seconds) 

其他C++ 11个的标准功能,可能会感兴趣的包括timed_mutexpromise/future

0

以下是一种方法。我使用C++ 11,线程,原子和高精度时钟。调度器将回调一个函数,该函数需要dt秒,这是自上次调用以来已过去的时间。如果回调函数返回false,则可以通过调用stop()方法来停止循环。

调度代码

#include <thread> 
#include <chrono> 
#include <functional> 
#include <atomic> 
#include <system_error> 

class ScheduledExecutor { 
public: 
    ScheduledExecutor() 
    {} 
    ScheduledExecutor(const std::function<bool(double)>& callback, double period) 
    { 
     initialize(callback, period); 
    } 
    void initialize(const std::function<bool(double)>& callback, double period) 
    { 
     callback_ = callback; 
     period_ = period; 
     keep_running_ = false; 
    } 

    void start() 
    { 
     keep_running_ = true; 
     sleep_time_sum_ = 0; 
     period_count_ = 0; 
     th_ = std::thread(&ScheduledExecutor::executorLoop, this); 
    } 

    void stop() 
    { 
     keep_running_ = false; 
     try { 
      th_.join(); 
     } 
     catch(const std::system_error& /* e */) 
     { } 
    } 

    double getSleepTimeAvg() 
    { 
     //TODO: make this function thread safe by using atomic types 
     //right now this is not implemented for performance and that 
     //return of this function is purely informational/debugging purposes 
     return sleep_time_sum_/period_count_; 
    } 

    unsigned long getPeriodCount() 
    { 
     return period_count_; 
    } 

private: 
    typedef std::chrono::high_resolution_clock clock; 
    template <typename T> 
    using duration = std::chrono::duration<T>; 

    void executorLoop() 
    { 
     clock::time_point call_end = clock::now(); 
     while (keep_running_) { 
      clock::time_point call_start = clock::now(); 
      duration<double> since_last_call = call_start - call_end; 

      if (period_count_ > 0 && !callback_(since_last_call.count())) 
       break; 

      call_end = clock::now(); 

      duration<double> call_duration = call_end - call_start; 
      double sleep_for = period_ - call_duration.count(); 
      sleep_time_sum_ += sleep_for; 
      ++period_count_; 
      if (sleep_for > MinSleepTime) 
       std::this_thread::sleep_for(std::chrono::duration<double>(sleep_for)); 
     } 
    } 

private: 
    double period_; 
    std::thread th_; 
    std::function<bool(double)> callback_; 
    std::atomic_bool keep_running_; 

    static constexpr double MinSleepTime = 1E-9; 

    double sleep_time_sum_; 
    unsigned long period_count_; 
}; 

示例用法

bool worldUpdator(World& w, double dt) 
{ 
    w.update(dt); 
    return true; 
} 

void main() { 
    //create world for your simulator 
    World w(...); 

    //start scheduler loop for every 2ms calls 
    ScheduledExecutor exec; 
    exec.initialize(
     std::bind(worldUpdator, std::ref(w), std::placeholders::_1), 
      2E-3); 
    exec.start(); 

    //main thread just checks on the results every now and then 
    while (true) { 
     if (exec.getPeriodCount() % 10000 == 0) { 
      std::cout << exec.getSleepTimeAvg() << std::endl; 
     } 
    } 
} 

还有other,上SO related问题。