4

整数の配列を返す独自の COM ライブラリがあります (もちろん、独自の独自形式で)。メイン UI スレッドからこの配列にアクセスすると、すべて問題なく、すばやく実行されます。別のスレッドからアクセスすると、アクセスが非常に遅くなります。以下にいくつかのサンプルコードがあります。

private void test() {
    ProprietaryLib.Integers ints = ProprietaryLib.GetInts();
    int x;
    for(int i = 0; i < 500; i++)
        for(int j = 0; j < ints.Count; j++)
            x = ints[j];
}

private void button1_Click(object sender, EventArgs e) {
    test();  // Very little time
    new System.Threading.Thread(() => test()).Start(); // Lots of time
}

なぜこうなった?これをスピードアップする方法はありますか?マルチスレッドの代わりにマルチプロセッシングを使用した場合、パフォーマンスが向上する見込みはありますか? (うーん、もっと複雑に聞こえます。)

アップデート:

以下の回答に満足していますが、参考のためにここにいくつかのデータを追加したいと思いました(私自身と他の人のもの)。

上記のように新しいスレッドでオブジェクトを作成してアクセスすると、アクセスごとに約 12ns になります。おそらく、オブジェクトは実際にはメインスレッドで作成され、速度が遅いのはそこからデータをマーシャリングするためです。

メイン スレッドで明示的にデータを作成し、シングル スレッド アパートメントとしてマークされた新しいスレッドでアクセスすると、アクセス時間はさらに遅くなり、アクセスあたり 15 ns になります。.NET には、アパートを快適に保つための追加のオーバーヘッドが必要だと思いますが、そのオーバーヘッドが何であるかわからないことが心配です。わずか 2 ~ 3 ns の差があれば、それほど大きくなる必要はありません。

STA とマークされた新しいスレッドでオブジェクトを作成してアクセスすると、時間はアクセスごとに 0.2ns で溶けます。しかし、この新しいスレッドは本当に安全ですか? それは私が思う別の質問に対する質問です。

4

3 に答える 3

8

COM オブジェクトにはスレッド アフィニティがあり、スレッド セーフではないことを COM に伝えることができます。レジストリのキーで、「ThreadingModel」キー。ほとんどの場合、"Apartment" を指定するか、単にキーを省略します。.NET ではあまり明示的ではなく、MSDN を使用して、クラスがスレッド セーフではないことを伝え、記事を読み忘れたことを思い出させません。.NET クラスの大部分はスレッドセーフではなく、COM コクラスと同じです。.NET とは異なり、COM はスレッド セーフな方法で呼び出されるようにします。オブジェクトを作成したスレッドへの呼び出しを自動的にマーシャリングします。

つまり、同時実行性がなく、非常に低速です。

前進する唯一の方法は、独自の Thread を作成し、その SetApartmentState() メソッドを呼び出して STA に切り替えることです。これは、スレッドセーフではない COM オブジェクトのハッピー ホームです。また、そのスレッドにも COM オブジェクトを作成する必要があります。また、STA の要件であるメッセージ ループを維持するためにポンプする必要がある場合もあります。また、スレッドをブロックしないでください。これらは、すべての呼び出しが1 つのスレッドで行われる場合、スレッド セーフではないクラスのホームとして最適なものです。この回答では、そのようなスレッドの実装例を見つけることができます。

つまり、スレッドセーフではないオブジェクトでスレッドを使用する場合、フリーランチはありません。.NET では、必要に応じてロックを使用するのを忘れて足を撃つことができますが、COM ではそれが自動化されます。そのように片方の足で飛び跳ねるプログラマーははるかに少なくなりますが、効率的ではありません。

于 2012-05-18T00:14:34.533 に答える
2

それは糸脱毛アパートのモデルに依存するかもしれません。シングルスレッドアパートメントモデル(STA)を使用している場合、パフォーマンスの問題が発生する可能性があります(データサイズが十分に大きい場合)。可能であれば(STAを必要とする別のCOMオブジェクトを使用していない場合)、アパートメントモデルをMTA(マルチスレッドアパートメントモデル)に変更してみてください。

注:WinFormsはMTAと互換性がありません。使用する一部のCOMオブジェクト(クリップボードやドラッグアンドドロップなど)で必要になるため、アパートメントモデルがシングルスレッドであることを常に確認します。私は試したことはありませんが、その機能を使用しない場合はおそらく機能します。

MSDNから:

オブジェクトへの呼び出しはシリアル化されないため、マルチスレッドオブジェクトの同時実行は最高のパフォーマンスを提供し、クロススレッド、クロスプロセス、およびクロスマシンの呼び出しにマルチプロセッサハードウェアを最大限に活用します。


SOに関するその他の参考資料: STAとMTAについて説明していただけますか?
MSDN:MTAThreadAttribute

于 2012-05-17T21:20:55.103 に答える
1

以下を使用して、UI スレッドで COM 呼び出しを実行してみてくださいInvoke()

private void button1_Click(object sender, EventArgs e) {
    ThreadPool.QueueUserWorkItem(delegate {
        this.Invoke((Action)(() => {
            test();
        }));
    });
}

この への呼び出しの前後に残りの長時間実行操作を実行してInvoke()、クイック COM 呼び出しのみが UI スレッドで実行されるようにします。また、何をしているかにもよりますが、多くのブラケットやその他のライン ノイズを取り除くことができます。

于 2012-05-17T21:24:43.757 に答える