適切に動作しないマルチスレッド アプリケーションの一般的なパターンCommon Patterns for Poorly-Behaved Multithreaded Applications

開発時に同時実行ビジュアライザーを使用すると、マルチスレッド アプリケーションの動作を可視化できます。The Concurrency Visualizer helps developers to visualize the behavior of a multithreaded application. このツールには、動作が不適切なマルチスレッド アプリケーションの一般的なパターンのギャラリーが含まれています。This tool includes a gallery of common patterns for multithreaded applications that behave badly. ギャラリーには、ツールで判明する一般的で認識可能な視覚パターンと、各パターンが示す動作、その動作から考えられる結果、その最も一般的な解決策の説明が含まれています。The gallery includes typical and recognizable visual patterns that are exposed through the tool, together with an explanation of the behavior that is represented by each pattern, the likely result of that behavior, and the most common approach to resolve it.

ロック競合とシリアル化実行Lock Contention and Serialized Execution

シリアル化実行でのロック競合の結果Lock Contention Resulting in Serialized Execution

複数のスレッドがあり、コンピューターに十分な数の論理コアがある場合でも、並列化されたアプリケーションがシリアル実行を継続することがあります。Sometimes a parallelized application stubbornly continues to execute serially even though it has several threads and the computer has a sufficient number of logical cores. まず、マルチスレッド化されたパフォーマンスの低下という現象 (シリアル実装よりもやや遅い可能性がある) が発生します。The first symptom is poor multithreaded performance, perhaps even a bit slower than a serial implementation. スレッド ビューには、並列実行されている複数のスレッドは表示されず、常に実行されている 1 つのスレッドのみが表示されます。In Threads View, you do not see multiple threads running in parallel; instead, you see that only one thread is executing at any time. この時点でスレッド内の同期セグメントをクリックすると、ブロックされたスレッド (コール スタックのブロック) のコール スタックと、ブロック条件を削除するスレッド (コール スタックのブロック解除) が表示されます。At this point, if you click a synchronization segment in a thread, you can see a call stack for the blocked thread (blocking call stack) and the thread that removed the blocking condition (unblocking call stack). また、分析しているプロセスでコール スタックのブロック解除が発生すると、スレッド対応のコネクタが表示されます。In addition, if the unblocking call stack occurs in the process that you are analyzing, a Thread-Ready connector is displayed. ここからコール スタックのブロックとブロック解除のコードに移動し、シリアル化の原因をさらに詳しく調べることができます。From this point, you can navigate to your code from the blocking and unblocking call stacks to investigate the cause of serialization even more.

同時実行ビジュアライザーを使用すると、次の図のように、[CPU 使用状況] ビューでこの現象を確認することもできます。[CPU 使用状況] ビューでは、複数のスレッドが存在する場合でも、1 つの論理コアのみが使用されます。As shown in the following illustration, the Concurrency Visualizer can also expose this symptom in the CPU Utilization View, where, despite the presence of multiple threads, the application consumes only one logical core.

詳細については、MSDN ブログ Web サイトの Hazim Shafi のブログ「Parallel Performance Tools For Windows」(Windows 用並列パフォーマンス ツール) の「Performance Pattern 1: Identifying Lock Contention」(パフォーマンス パターン 1: ロック競合の特定) を参照してください。For more information, see "Performance Pattern 1: Identifying Lock Contention" in Hazim Shafi's Parallel Performance Tools For Windows blog on the MSDN blog Web site.

ロック競合Lock Contention

不均一なワークロード分散Uneven Workload Distribution

不均一なワークロードUneven Workload

アプリケーションの複数の並列スレッドで作業の不規則な分散が発生している場合、前の図のように、各スレッドの作業が完了するときに一般的な階段パターンが発生します。ほとんどの場合、同時実行ビジュアライザーに表示される各同時実行スレッドの開始時間はとても近接しています。When an irregular distribution of work occurs across several parallel threads in an application, a typical stair-step pattern appears as each thread completes its work, as shown in the previous illustration.The Concurrency Visualizer most often shows very close start times for each concurrent thread. 一方で、同時実行スレッドは同時に終了するのではなく、不規則に終了するのが一般的です。However, these threads typically end in an irregular manner instead of ending simultaneously. このパターンは、並列スレッドのグループ内で作業の分散が不規則であることを示します。これがパフォーマンス低下の原因にもなります。This pattern indicates an irregular distribution of work among a group of parallel threads, which could lead to decreased performance. このような問題の最適なアプローチは、並列スレッドで作業を分散するアルゴリズムを再評価することです。The best approach to such a problem is to reevaluate the algorithm by which work has been divided among the parallel threads.

次の図のように、同時実行ビジュアライザーの [CPU 使用状況] ビューでは、CPU 使用状況の段階的な低下としてこの現象を確認できます。As shown in the following illustration, the Concurrency Visualizer can also expose this symptom in the CPU Utilization View as a gradual step-down in CPU utilization.

不均一なワークロードUneven Workload



オーバーサブスクリプションが発生している場合、プロセス内のアクティブなスレッド数がシステムの空き論理コア数よりも多くなっています。In the case of oversubscription, the number of active threads in a process is larger than the number of available logical cores on the system. 前の図は、すべてのアクティブなスレッドで重要な優先度の縞模様が表示されているオーバーサブスクリプションの結果を示しています。The previous illustration shows the results of oversubscription, with significant preemption banding in all active threads. また、凡例には、大部分の時間が "優先" に使われていることが示されています (この例では 84%)。In addition, the legend shows a large percentage of time is spent in Preemption (84 percent in this example). これは、プロセスがシステムに対して、論理コア数よりも多くの同時実行スレッドを実行するように求めていることを示す可能性があります。This may indicate that the process is asking the system to execute more concurrent threads than the number of logical cores. ただし、このプロセスに使用されるはずだったリソースを、システム上の他のプロセスが使用していることを示す可能性もあります。However, this may also indicate that other processes on the system are using resources that were assumed to be available to this process.

この問題を評価する際は、次の点を考慮してください。You should consider the following when you evaluate this problem:

  • システム全体でオーバーサブスクリプションが発生している可能性があります。The overall system may be oversubscribed. システム上の他のプロセスが対象のスレッドを優先している可能性を考慮してください。Consider that other processes on the system may be preempting your threads. スレッド ビューの優先セグメントにカーソルを合わせると、スレッドとそのスレッドを優先したプロセスを示すツールヒントが表示されます。When you pause over a preemption segment in the threads view, a tooltip will identify the thread and the process that preempted the thread. このプロセスは、自分のプロセスが優先された時間中ずっと実行されていたプロセスではない場合がありますが、自分のプロセスに対して優先の負荷をかけているプロセスのヒントとして考えます。This process is not necessarily the one that executed during the whole time that your process was preempted, but it provides a hint about what created the preemption pressure against your process.

  • この作業のフェーズでは、自分のプロセスが実行に使用できる適切なスレッド数を決定する方法を評価してください。Evaluate how your process determines the appropriate number of threads for execution during this phase of work. アクティブな並列スレッド数をプロセスが直接計算している場合は、システムの空き論理コア数をより適切に考慮するようにアルゴリズムを変更することをお勧めします。If your process directly calculates the number of active parallel threads, consider modifying that algorithm to better account for the number of available logical cores on the system. 同時実行ランタイム、タスク並列ライブラリ、PLINQ を使用している場合、これらのライブラリがスレッド数の計算作業を実行します。If you use the Concurrency Runtime, the Task Parallel Library, or PLINQ, these libraries perform the work of calculating the number of threads.

非効率的な I/OInefficient I/O

非効率な I/OInefficient I/O

I/O の過度な使用や誤使用は、アプリケーションの非効率の一般的な原因です。Overuse or misuse of I/O is a common cause of inefficiencies in applications. 前の図を例にして説明します。Consider the previous illustration. [表示されているタイムライン プロファイル] には、表示されているスレッド時間の 42% が I/O に使用されています。The Visible Timeline Profile shows that 42 percent of the visible thread time is consumed by I/O. タイムラインには大量の I/O が表示され、プロファイリング対象のアプリケーションが I/O で頻繁にブロックされていることがわかります。The timeline shows large amounts of I/O, which indicates that the profiled application is frequently blocked by I/O. I/O の種類とプログラムがブロックされている場所の詳細を確認するには、問題が発生した領域を拡大して、[表示されているタイムライン プロファイル] を確認し、特定の I/O ブロックをクリックして現在のコール スタックを表示します。To see details about the kinds of I/O and where your program is blocked, zoom into problematic regions, examine the Visible Timeline Profile, and then click a specific I/O block to see current call stacks.

ロック コンボイLock Convoys

ロック コンボイLock Convoys

ロック コンボイは、早い者勝ちの順序でアプリケーションがロックを取得したときと、ロックの到着率が取得率よりも高いときに発生します。Lock convoys occur when the application acquires locks in a first-come, first-served order, and when the arrival rate at the lock is higher than the acquisition rate. これら 2 つの条件が組み合わさると、ロックの要求によってバックアップが開始されます。The combination of these two conditions causes requests for the lock to start backing up. この問題を解決するには、"不公平な" ロック (最初のスレッドにロック解除状態で検索するアクセス権を与えるロック) を使用する方法があります。One way to combat this problem is to use "unfair" locks, or locks that give access to the first thread to find them in unlocked states. 前の図は、このコンボイ動作を示しています。The previous illustration shows this convoy behavior. この問題を解決するには、同期オブジェクトの競合を減らし、不公平なロックを使用してみてください。To solve the problem, try reducing contention for the synchronization objects and try using unfair locks.

参照See Also

スレッド ビューThreads View