4

マルチスレッドを使用して、C++ の Linux でシリアル IO を実行しています。現在、ブロッキング読み取りを使用しています。そのため、強制的にスレッドを終了または中断するか、pthread キャンセルのようなものを使用する以外に、ブロックしている read() でスレッドの咳を止める方法はありません。現在、ネット全体で、ブロッキングIOからスレッドを終了する必要があると人々が叫んでいるのを目にします。通常、メモリリークが関係しています。適切にクリーンアップする限り、スレッドの中断から発生する可能性のある魔​​法のメモリリークはありますか?

try
{
    while(true)
    {
        blocking_read(fd,buffer,512);
    }
}catch(interrupt_exception)
{

}
//clean up, close fd, release heap memory, usual stuff

または、以下のような私の唯一の代替手段であるか、より高いレベルのプロトコルを実装して、ブロッキング読み取りがサインオフ入力を受け取り、それをシャットダウンできるようにします。

try
{
    while(running)
    {
        nonblocking_read(fd,buffer,512);

        if(cancel)
            running = false; //break return etc
    }
}

//clean up, close fd, release heap memory, usual stuff

繰り返しになりますが、スレッドを中断して例外をスローすると、 read() で魔法のメモリリークが発生しますか。

または、単に気にせず、デストラクタにスレッドを強制終了させる必要があります(スレッドを保持しているオブジェクトを削除すると、スレッドが終了すると仮定します)? そこを片付けますか?お気に入り

class MyClass{
    int fd;    
    Thread* myThread;
    ~MyClass(){
        delete myThread;
        close(fd);
    }
};

助けてくれてありがとう!

4

3 に答える 3

6

read()メモリをリークしないでください。ブロッキング読み取りと非ブロッキング読み取りの両方で、アプリケーションコードは、buf引数として提供されるメモリの管理を引き続き担当します。シグナルを介した割り込みread()は例外をスローしないため、シグナルを使用する場合は、結果とerrnoを確認する必要があります。

  • read()データを読み取る前にが信号によって中断された場合、 errnoがに設定された-1が返されますEINTR
  • read()一部のデータの読み取り後にがシグナルによって中断された場合、POSIXでは、 errnoを設定して-1を返すEINTRか、read()すでに読み取られたバイト数を返すことができます。

を使用するpthread_cancel()と、例外がスローされます。このアプローチでは、次のオプションがあります。

  • クリーンアップ関数をに登録してクリーンアップを実行しpthread_cleanup_push()ます。
  • 動的メモリを割り当て、を介してスレッド固有のストレージに保存しpthread_setspecific()ます。
  • を介してメモリを管理しますauto_ptr/unique_ptr
  • 例外をキャッチし、abi::__forced_unwindクリーンアップを実行して、再スローします。

一般に、スレッドのキャンセルは避けることを検討してください。可能であれば、ループから抜け出すために使用される共有フラグを使用する方が適切であり、管理しやすくなります。これにより、スレッドは必要なクリーンアップを実行できるようになり、実装がスレッドライブラリの実装とその癖に依存するのを防ぐことができます。

ブロッキング読み取りを使用している場合は、fdをポーリングしselect()て、タイムアウト付きでデータが利用可能かどうかを確認し、fdにデータがある場合にのみ呼び出すことを検討してくださいread()。これにより、スレッドフラグが実行されなくなるように設定されているかどうかを定期的に確認でき、データの待機をブロックしなくなるためread()、スレッドを中断するためのシグナルを処理する必要がなくなります。read()

また、スレッドオブジェクトを削除するときに発生する動作は、スレッドライブラリによって異なります。たとえば、pthread_tまたはboost::threadを削除しても、関連するスレッドの実行には影響しません。

于 2012-05-31T15:44:32.737 に答える
1

pthread_cancel が魔法のメモリ リークを直接引き起こすことはありません。明確に定義されたキャンセル ポイント (詳細については pthread_cancel のマニュアル ページを参照) でスレッドが停止することを期待している場合は、これを念頭に置いて設計することができます。

次のテスト プログラムは valgrind でエラーを示していませんが、glibc 実装のスレッドがキャンセルされたときに生成される例外に依存することは移植性がないことに注意してください (「キャンセルと C++ 例外」を参照)。

-ニック

#include <cstdio>
#include <pthread.h>
#include <unistd.h>

void* thread_func(void*)
{
    try
    {
        char buf[2048];
        read(1, buf, 2048);
    }
    catch(...)
    {
        fprintf(stderr, "thread %lu cancelled\n", pthread_self());
        throw;
    }
    return NULL;
}

int main()
{
    pthread_t thread;
    int res =  pthread_create(&thread, NULL, thread_func, NULL);
    sleep(1);
    pthread_cancel(thread);
    void* tres;
    pthread_join(thread, &tres);
    return 0;
}
于 2012-05-31T14:12:58.630 に答える
0

私は本当にそのスレッドにシグナルを送信するだけです。それがシグナルの目的です。グローバルrunningfalseハンドラーに設定while(running)し、2 番目のスニペットのようなループを作成します。それだけです。

readリークしません。すでに割り当てられており、知っているバッファに読み込まれます。成功または失敗します (またはブロックしますが、シグナルはブロックを解除します)。成功した場合は、データから何ができるかを確認し (まだ部分的に読み取られているか、破損している可能性があります)、そうでない場合は になるまで繰り返しrunningますfalse

次に、クリーンアップし、割り当てたものを解放し、スレッドを正常に終了します (... または、必要なことを行います)。

私が間違っていなければ、システムコールの再起動に関するかなり複雑な「データを受信する前後」の状況は、 を提供せずにハンドラーをインストールするときにもまったく発生しませんSA_RESTART
しかし、たとえそうであったとしても、誰が気にするかというと、結局のところ、syscall は失敗するか成功するかのどちらかでしかなく、シグナルがなくても常にread部分的なデータを返すことができるので、いずれにせよ常に準備をしておく必要があります。したがって、実際に関心があるのは、何かを有効にするのに十分なデータをすべて収集したかどうか、または何らかの理由でフラグが「魔法のように」なったかどうかだけです (その理由はシグナル ハンドラーです)。runningfalse

于 2012-06-01T10:19:06.417 に答える