3

私はメインスレッドの上に2つのスレッドがある画像処理アプリケーションに取り組んでいます:

1- Webカメラから画像をキャプチャしてバッファに書き込むCameraThread

2-フィルタリングのためにそのバッファから最新の画像を取得するImageProcessingThread 。

これがマルチスレッドである理由は、速度が重要であり、前の画像を処理している間、画像を取得し、ImageProcessingThreadが最新のキャプチャを取得できるようにするためにCameraThreadが必要だからです。

私の問題は、その共通バッファーにアクセスするための高速でスレッドセーフな方法を見つけることです。理想的には、ImageProcessingThreadが遅い場合に、CameraThreadが続行できるように、トリプルバッファー(image [3])である必要があると考えました。他の2つの画像に書き込む、またはその逆。

これがスレッドセーフであるためには、どのような種類のロックメカニズムが最も適切でしょうか?

私はlockステートメントを調べましたが、スレッドがブロックされ、別のスレッドが終了するのを待っているように見えます。これは、トリプルバッファリングのポイントに反します。

アイデアやアドバイスを事前に感謝します。

J。

4

7 に答える 7

8

これは、生産者/消費者パターンの教科書の例である可能性があります。

.NET 4で作業する場合は、IProducerConsumerCollection<T>および関連する具象クラスを使用して機能を提供できます。

そうでない場合は、パターンの詳細についてこの記事を読み、ブロッキング先入れ先出し構造の独自のスレッドセーフな実装を作成するためのガイダンスとしてこの質問を読んでください。

于 2010-03-25T15:31:30.980 に答える
3

個人的には、アクセスを管理する必要がある一元化された「バッファ」に書き込むのではなく、これに対する別のアプローチを検討したいと思うかもしれません。イベントを使用するアプローチに切り替えることができます。カメラスレッドが画像を「受信」すると、イベントが発生し、画像データが実際に画像処理を処理するプロセスに渡される可能性があります。

別の方法は、キューを使用することです。キューはFIFO(First in First Out)データ構造であり、アクセスに対してスレッドセーフではないため、ロックする必要がありますが、ロック時間は非常に短くなります。アイテムをキューに入れます。スレッドセーフで使用できる他のキュークラスもあります。

あなたのアプローチを使用すると、あなたが対処しなければならないであろう多くの問題があります。アレイにアクセスしているときのブロッキング、使用可能なアレイスロットが不足した後に何が起こるかに関する制限、ブロッキングなど。

于 2010-03-25T15:25:46.547 に答える
2

写真に必要な歳差運動の量を考えると、単純なロック方式がボトルネックになるとは思いません。間違った問題に時間を浪費し始める前に測定してください。
「ロックフリー」ソリューションには十分注意してください。見た目よりも常に複雑です。

そして、配列ではなくキューが必要です。
dotNET4を使用できる場合は、ConcurrentQuueを使用します。

于 2010-03-25T15:37:25.413 に答える
1

いくつかのパフォーマンスメトリックを実行する必要がありますが、を見てくださいlock free queues

たとえば、この質問とそれに関連する回答を参照してください。

ただし、特定のアプリケーションでは、プロセッサは最新のイメージにのみ関心があります。事実上、これは、読み取りと書き込みの間に競合がないように、2つのアイテム(新しいアイテムと前のアイテム)のキューのみを維持したいことを意味します。たとえば、新しいエントリが書き込まれたら、プロデューサーにキューから古いエントリを削除させることができます。

編集:これをすべて言ったので、ミッチェルセラーズの答えで言われていることには多くのメリットがあると思います。

于 2010-03-25T15:24:49.107 に答える
0

ただのアイデア。

話しているのは2つのスレッドだけなので、いくつかの仮定を立てることができます。

トリプルバッファのアイデアを使用してみましょう。ライタースレッドとリーダースレッドが1つしかない場合、整数の形式で「フラグ」を前後に投げることができます。両方のスレッドは継続的にスピンしますが、バッファーを更新します。警告:これは1つのリーダースレッドでのみ機能します

擬似コード

共有変数:

intステータス=0; //0=書き込み準備完了; 1=読む準備ができている

Buffer1=新しいバイト[]

Buffer2=新しいバイト[]

Buffer3=新しいバイト[]

BufferTmp = null

thread1{

while(true)
{
    WriteData(Buffer1);
    if (Status == 0)
    {
        BufferTmp = Buffer1;
        Buffer1 = Buffer2;
        Buffer2 = BufferTmp;
        Status = 1;
    }
}

}

thread2{

while(true)
{
    ReadData(Buffer3);
    if (Status == 1)
    {
        BufferTmp = Buffer1;
        Buffer2 = Buffer3;
        Buffer3 = BufferTmp;
        Status = 0;
    }
}

}

writedataメソッドは新しいバイトオブジェクトを作成しませんが、現在のバイトオブジェクトを更新することを覚えておいてください。新しいオブジェクトの作成には費用がかかります。

また、ELSEステートメントのthread.sleep(1)をIFステートメントに添付することもできます。そうでない場合、1つのシングルコアCPUの場合、スレッドが回転すると、他のスレッドがスケジュールされる前にレイテンシが増加します。例えば。スケジューラーは書き込みスレッドが「作業」を行っていることを確認するため、書き込みスレッドは読み取りスレッドがスケジュールされる前に2〜3回スピンを実行する可能性があります

于 2010-03-26T18:23:42.613 に答える
0

書き込みの高速読み取りとアップグレード可能なロックを可能にするReaderWriterLockSlimの使用を検討します。

于 2010-03-25T15:27:32.703 に答える
0

これはあなたの質問に対する直接の答えではありませんが、並行性モデルを再考する方が良いかもしれません。ロックは、レベルが低すぎる、エラーが発生しやすいなど、あらゆるものを同期するためのひどい方法です。メッセージパッシングの同時実行性の観点から問題を再考してみてください。

ここでの考え方は、各スレッドが独自の緊密に含まれたメッセージループであり、各スレッドにメッセージを送受信するための「メールボックス」があるということです。これらのタイプのオブジェクトをプレーンなジェーンスレッドと区別するためにMailboxThreadという用語を使用します。

したがって、2つのスレッドが同じバッファにアクセスする代わりに、2つのMailboxThreadsが相互にメッセージを送受信します(擬似コード)。

let filter =
    while true
        let image = getNextMsg() // blocks until the next message is recieved
        process image

let camera(filterMailbox) =
    while true
        let image = takePicture()
        filterMailbox.SendMsg(image) // sends a message asyncronous

let filterMailbox = Mailbox.Start(filter)
let cameraMailbox = Mailbox.Start(camera(filterMailbox))

今、あなたはスレッドを処理しているので、バッファをまったく知らないか、気にしません。彼らはただメッセージを待って、それらが利用可能であるときはいつでもそれらを処理します。filterMailboxが処理するために多くのメッセージを送信すると、それらのメッセージは後で処理されるようにキューに入れられます。

ここで難しいのは、実際にMailboxThreadオブジェクトを実装することです。正しく行うにはある程度の創造性が必要ですが、これらのタイプのオブジェクトを実装して、メッセージの処理中にスレッドを開いたままにし、処理するメッセージが残っていないときに実行中のスレッドをスレッドプールに解放することは完全に可能です。 (この実装により、スレッドをぶら下げることなくアプリケーションを終了できます)。

ここでの利点は、スレッドがロックや同期を気にせずにメッセージを送受信する方法です。舞台裏では、メッセージのエンキューまたはデキューの間にメッセージキューをロックする必要がありますが、その実装の詳細はクライアント側のコードに対して完全に透過的です。

于 2010-03-25T15:43:49.957 に答える