2014-10-27 68 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問題。