2016-07-07 15 views
0

私はコンピュータ上のシリアルポートを読み取り、このデータを周波数スペクトルに変換するためにfftを実行する、ある種のオシロスコープエスクプログラムを作成しています。私はSerialHandlerクラス(boost::Asioを利用しています)、FFTHandlerクラス、およびmainの関数に分割された私のプログラムのレイアウトで問題に遭遇しました。 SerialHandlerクラスは、boost::Asio`` async_read_some関数を使用してポートから読み取り、HandleOnPortReceiveというイベントを発生させ、データ自体を読み取ります。C++セマフォの混乱?

問題は、別のスレッドのio_serviceオブジェクトによって生成されたイベントハンドラからそのデータを、別のスレッドのFFTHandlerクラスに渡す方法が見つからないことでした。私は問題を解決するためにセマフォを使用することが推奨されましたが、私はsemaphore.hの使用法に関する知識がないので、実装は今やかなり壊れていて、それは想定していたものをほとんど行いません。

それはそれは少し明確になります場合はここにいくつかのコードです:

using namespace Foo; 
//main function  
int main(void){ 
    SerialHandler serialHandler; 
    FFTHandler fftHandler; 

    sem_t *qSem_ptr = &qSem; 

    sem_init(qSem_ptr, 1, 0); 

    //create separate threads for both the io_service and the AppendIn so that neither will block the user input statement following 
    serialHandler.StartConnection(tempInt, tempString); //these args are defined, but for brevity's sake, I ommitted the declaration 

    t2= new boost::thread(boost::bind(&FFTHandler::AppendIn, &fftHandler, q, qSem)); 

    //allow the user to stop the program and avoid the problem of an infinite loop blocking the program 
    char inChar = getchar(); 
    if (inChar) {...some logic to stop reading} 
} 




namespace Foo{ 

    boost::thread *t1; 
    boost::thread *t2; 
    sem_t qSem; 
    std::queue<double> q; 
    boost::mutex mutex_; 

    class SerialHandler{ 
    private: 
     char *rawBuffer; //array to hold incoming data 
     boost::asio::io_service ioService; 
     boost::asio::serial_port_ptr serialPort; 
    public: 

      void SerialHandler::StartConnection(int _baudRate, string _comPort){ 
       //some functionality to open the port that is irrelevant to the question goes here 

       AsyncReadSome(); //starts the read loop 

       //create thread for io_service object and let function go out of scope 
       t1 = new boost::thread(boost::bind(&boost::asio::io_service::run, &ioService)); 

      } 

      void SerialHandler::AsyncReadSome(){ 

       //there's some other stuff here for error_catching, but this is the only important part 
       serialPort->async_read_some (
          boost::asio::buffer(rawBuffer, SERIAL_PORT_READ_BUF_SIZE), 
          boost::bind(
            &SerialHandler::HandlePortOnReceive, 
            this, boost::asio::placeholders::error, 
            boost::asio::placeholders::bytes_transferred, q)); 
      } 

      void SerialHandler::HandlePortOnReceive(const boost::system::error_code& error, size_t bytes_transferred, std::queue<double>& q){ 
       boost::mutex::scoped_lock lock(mutex_); 
       //more error checking goes here, but I've made sure they aren't returning and are not the issue 

       for (unsigned int i =0; i<bytes_transferred; i++){ 
        unsigned char c = rawBuffer[i]; 
        double d = (double) c; //loop through buffer and read 
        if (c==endOfLineChar){ 
        } else //if not delimiting char, push into queue and post semaphore 
        { 
          q.push(d); 
          //cout << d << endl; 
          sem_post(&qSem); 
          cout << q.front() << endl; 
          cout << "size is: " << q.size() << endl; 
        } 
       } 
       //loop back on itself and start the next read 
       AsyncReadSome(); 

      } 
    } 

    class FFTHandler{ 
    private: 
     double *in; //array to hold inputs 
     fftw_complex *out; //holds outputs 
     int currentIndex; 
     bool filled; 
     const int N; 
    public: 


     void AppendIn(std::queue<double> &q, sem_t &qSem){ 
       while(1){ //this is supposed to stop thread from exiting and going out of scope...it doesn't do that at all effectively... 
        cout << "test" << endl; 
        sem_wait(&_qSem); //wait for data...this is blocking but I don't know why 
        double d = _q.front(); 
        _q.pop(); 
        in[currentIndex]=d; //read queue, pop, then append in array 
        currentIndex++; 
        if (currentIndex == N){ //run FFT if full and reset index 
          currentIndex = N-overlap-1; 
          filled = true; 
          RunFFT(); 
        } 
       } 

     } 
    } 

} 

FFTHandler::AppendIn(..)でのデバッグ行が実際に発射され、そのスレッドが作成されているが、それはimmediateleyスコープ外になるだろうそれはそうと破壊こと私はセマフォに間違って応答するように設定しているようだからです。

TLDR:。それは今、私はうまくいけば、このコードのヘルプを受信するためにここに来て、私が試した、失敗した、単に私がセマフォを理解何とかそれらを実装する必要はありません」、と言うのに長い説明でした私よりも精通して誰かから

UPDATE:だから、いくつかのデバッグ文で遊んでた後、それは問題がwhile(1){...}文が実際に発射され、しかし、sem_wait(&_qSem);はそれをブロックする原因になっているということのようです何のために。セマフォがポストされているにもかかわらず、無期限に待機している理由は、待ち続けており、その行を超えて進んでいることはありません。

+1

_semaphore usage_に間違ったものは何も見えませんが、スレッディングの潜在的な問題を見ることができます - スレッドが終了する前に 'main'が終了していますか? (ちょうど "...いくつかのロジックが読み込みを停止する"というコードからはっきりしていません) –

+2

なぜSerialHandlerとFFTHandlerを別々のスレッドで実行していますか? SerialHandlerが次のデータセットを受け取れるように別のスレッドに作業を渡すことが意図されている場合は、Leader/Followersパターンを考慮し、スレッドのプールにio_serviceを実行させます。ここでは、各スレッドはSerialHandlerを使用してデータを読み込み、FFTHandlerを使用してデータを処理し、終了するとプールに戻り、次のioイベントを待機します。 – aichao

+0

@aichao私はハンドラ内でループバックするため、別のスレッドで実行しているSerialHandler io_serviceを持っています。したがって、無限ループが作成されます。しかし、私はプログラムを止めることができたいと思っています。無限ループはまったく望ましい機能ではありませんでしたので、ユーザーの入力を受け入れるために別のスレッドに移動しました。同様の理由から、 'FFTHandler :: AppendIn(...)'関数は、メインスレッドをブロックせず、プログラムの実行中にユーザーがプログラムを実行できるようにする(つまりプログラムを停止する)ために、 。 – Scorch

答えて

2

既にboost::mutexとそのスコープロックタイプを使用しているので、POSIXセマフォの代わりにboost::condition_variableを使用することをお勧めします。さもなければ、POSIX同期とC++ 11スタイルの同期を混在させます。

キューに追加するときにmutexをロックしますが、キューから読み込むためにmutexをロックするものはありません。また、ミューテックスがまだロックされている間に、あなたはループしてAsyncReadSomeに電話をかけているようです。

単一の形式の同期を選択し、正しく使用します。

+0

アドバイスありがとう、それはいくつかの混乱をクリアするようでした。私はセマフォを引っ張り、あなたのアイデアごとにboost :: condition_variableを使いました。これはうまくいくようです。それは別の問題(セグメンテーションフォールト)を導入しましたが、私が知る限りシグナリングと条件変数とあまり関係しません。 – Scorch

1

セマフォの初期値は0で、この場合に有効です。だから、FFTHandler::AppendIn()のブロックを解除するにはsem_postが必要です。しかし、シリアルポートが読み込まれ、プッシュがキューに入るのは、最初にSerialHandler::AsyncReadSome()を呼び出すコードが表示されません。コードのその部分を修正すると、sem_postが発生し、FFTHandlerスレッドが実行されると思います。最初のステップとして、sem_waitの後に1つ、AsyncReadSome()関数の中で1つずつデバッグすることができます。私の推測では、両方とも実行されることはありません。

本質的に、「読み込み」が開始され、メインスレッドまたは別のスレッドの一部として生きていることを確実にしたいと思うでしょう。

+0

私は謝罪します。それは私のせいです。私はコードをコピーするときに間違いを犯したことに気付きました。新しいスレッドt1を作成する直前にAsyncReadSome()を呼び出します。上記のコードを更新します。しかし、念のために、私はいくつかのデバッグ行を作成し、それらが起動してAsyncReadSome()を実行しています。それは、あなたの提案で周りを遊んでいる間、私はセマフォと関係がある他の興味深いものを見つけたので、私は質問にもそれを追加します。 – Scorch

+0

あなたがあなたのアップデートで作ったポイントは、まさに私が意味していたものです。 – Gops