スレッドはコードの実行を高速化できますが、実際に必要ですか?すべてのコードを単一のスレッドを使用して実行できますか、それとも複数のスレッドを使用することによってのみ達成できる何かが存在しますか?
まず、スレッドはコードの実行を高速化できません。それらはコンピュータをより速く走らせません。彼らができることは、さもなければ無駄になっていたであろう時間を使うことによってコンピュータの効率を高めることです。特定のタイプの処理では、この最適化により効率が向上し、実行時間が短縮されます。
簡単な答えはイエスです。シングルスレッドで実行するコードを書くことができます。証明:シングルプロセッサシステムは、直線的にのみ命令を実行できます。オペレーティングシステムが割り込みを処理し、現在のスレッドの状態を保存し、別のスレッドを開始することで、複数行の実行が行われます。
complexの答えは...より複雑です!マルチスレッドプログラムがリニアプログラムよりも効率的であることが多い理由は、ハードウェアの「問題」が原因です。 CPUは、メモリおよびハードストレージIOよりも高速に計算を実行できます。したがって、たとえば、「追加」命令は「フェッチ」よりもはるかに高速に実行されます。キャッシュと専用のプログラム命令フェッチ(ここでの正確な用語は不明)はある程度これに対抗できますが、速度の問題は残ります。
スレッド化は、IO命令が完了している間、CPUにバインドされた命令にCPUを使用することにより、この不一致に対処する方法です。典型的なスレッド実行計画は、おそらくデータのフェッチ、データの処理、データの書き込みです。想定説明のために、フェッチと書き込みには3サイクルかかり、処理には1サイクルかかります。コンピュータが読み取りまたは書き込みを行っている間、それぞれ2サイクル何も実行していないことがわかります。怠惰であり、私たちは最適化の鞭を割る必要があります!
この無駄な時間を使用するために、スレッドを使用してプロセスを書き直すことができます。
等々。明らかにこれはやや工夫された例ですが、この手法が他の方法でIOの待機に費やされる時間をどのように利用できるかを理解できます。
上記のようにスレッド化すると、効率が大幅に向上することに注意してくださいIOバインドされたプロセス。プログラムが主に物事を計算している場合、多くの「穴」はなく、 。また、スレッドを切り替えるときにいくつかの命令のオーバーヘッドがあります。実行するスレッドが多すぎると、CPUはほとんどの時間を切り替えに費やし、実際には問題にあまり取り組みません。これは thrashing と呼ばれます。 =。
シングルコアプロセッサにはこれで十分ですが、最近のほとんどのプロセッサには2つ以上のコアがあります。スレッドは同じ目的を果たします-CPUの使用を最大化するために、今回は同時に2つの個別の命令を実行する機能があります。これはできますコンピュータは実際にはマルチタスクであり、コンテキストの切り替えではないため、使用可能なコア数に応じて実行時間を短縮します。
複数のコアでは、スレッドは2つのコア間で作業を分割する方法を提供します。上記はまだ個々のコアにも当てはまります。 1つのコアで2つのスレッドを使用して最大効率を実行するプログラムは、2つのコアで約4つのスレッドを使用してピーク効率で実行する可能性が最も高くなります。 (ここでは、効率は最小NOP命令実行によって測定されます。)
(シングルコアではなく)複数のコアでスレッドを実行する場合の問題は、通常、ハードウェアによって処理されます。 CPUは、読み取り/書き込みを行う前に、適切なメモリ位置を確実にロックします。 (これのためにメモリ内で特別なフラグビットを使用することを読みましたが、これはいくつかの方法で達成できます。)高水準言語のプログラマーとして、2つのコアでこれ以上何も心配する必要はありません。 1つとする必要があります。
TL; DR:スレッドは作業を分割して、コンピューターが複数のタスクを非同期に処理できるようにします。これにより、プロセスがリソースを待機しているときにロックするのではなく、利用可能なすべての処理時間を利用して、コンピューターを最大効率で実行できます。
複数のスレッドで、単一のスレッドではできないことは何ですか?
何もない。
簡単な証明のスケッチ:
ただし、そこには大きな仮定が隠されていることに注意してください。つまり、使用される言語withinは、単一のスレッドがチューリング完全であることです。
したがって、より興味深い質問は、「チューリング完全でない言語にjustマルチスレッドを追加すると、チューリング完全にすることができるか?」そして、私は信じています、答えは「はい」です。
トータル関数型言語を取り上げましょう。 [よく知らない人のために:関数型プログラミングが関数を使ったプログラミングであるように、合計関数型プログラミングは関数全体を使ったプログラミングです。]
完全関数型言語は明らかにチューリング完全ではありません。TFPLで無限ループを作成することはできません(実際、これはdefinitionの「合計」です)が、-can = Turing Machineには、TFPLで記述できないがUTMで記述できるプログラムが少なくとも1つ存在するため、TFPLはUTMよりも計算能力が低くなります。
ただし、TFPLにスレッドを追加するとすぐに、無限ループが発生します。ループの各反復を新しいスレッドで実行するだけです。すべての個々のスレッドは常に結果を返すため、それはTotalですが、すべてのスレッドはnewスレッドを生成しますnext反復を実行し、無限に。
I thinkこの言語はチューリング完全であると思います。
少なくとも、元の質問に答えます。
複数のスレッドで、単一のスレッドではできないことは何ですか?
If無限ループを実行できない言語がある場合、thenマルチスレッドを使用すると、無限ループを実行できます。
もちろん、スレッドの生成は副作用であり、したがって拡張言語はもはやTotalではなく、Functionalでさえないことに注意してください。
理論的には、マルチスレッドプログラムが実行するすべてのことは、シングルスレッドプログラムでも実行できますが、速度は遅くなります。
実際には、速度の差が非常に大きくなる可能性があり、タスクにシングルスレッドプログラムを使用する方法はありません。例えば。バッチデータ処理ジョブを毎晩実行していて、シングルスレッドで終了するまでに24時間以上かかる場合、それをマルチスレッドにする以外に選択肢はありません。 (実際には、しきい値はおそらくさらに低くなります。そのような更新タスクは、ユーザーがシステムを再び使用し始める前に、早朝までに完了する必要があります。また、他のタスクがそれらに依存し、同じ夜中に完了する必要がある場合もあります。したがって、使用可能なランタイムは、数時間/分程度の低さになる場合があります。)
複数のスレッドで計算作業を行うことは、分散処理の一種です。作業を複数のスレッドに分散しています。分散処理の別の例(複数のスレッドではなく複数のコンピューターを使用)は、SETIスクリーンセーバーです。シングルプロセッサで大量の測定データを処理すると、非常に長い時間がかかり、研究者は退職前に結果を見ることを好みます;-)ただし、スーパーコンピューターをそれほど長く借りる予算がないので、スーパーコンピューターを数百万世帯のPCに分散させて、安価にしています。
スレッドは逐次計算からの小さなステップのように見えますが、実際には、それらは大きなステップを表しています。それらは、逐次計算の最も本質的で魅力的な特性、つまり、理解可能性、予測可能性、および決定論を破棄します。スレッドは、計算のモデルとして、非常に非決定的であり、プログラマーの仕事は、その非決定性の剪定の1つになります。
-スレッドの問題(www.eecs.berkeley.edu/Pubs/TechRpts/2006/EECS-2006-1.pdf)。
複数のコアに作業を分散できるという点で、スレッドを使用することで得られるパフォーマンス上の利点はいくつかありますが、多くの場合、価格が高くなります。
ここでまだ言及されていないスレッドを使用することの欠点の1つは、シングルスレッドのプロセススペースで得られるリソースのコンパートメント化が失われることです。たとえば、セグメンテーション違反が発生したとします。場合によっては、マルチプロセスアプリケーションでこれから回復することが可能です。これは、障害のある子を死なせて、新しい子を再生成するだけです。これは、Apacheのpreforkバックエンドの場合です。 1つのhttpdインスタンスがおかしくなった場合、最悪のケースでは、特定のHTTPリクエストがそのプロセスに対してドロップされる可能性がありますが、Apacheは新しい子を生成します。最終結果は、Apacheが全体として障害のあるスレッドによって停止されないことです。
このシナリオでのもう1つの考慮事項は、メモリリークです。スレッドのクラッシュを正常に処理できる場合があります(UNIXでは、特定の信号からの回復-segfault/fpviolation-でも可能です)が、その場合でも、そのスレッドによって割り当てられたすべてのメモリがリークしている可能性があります(malloc、newなど)。そのため、プロセスが存続している場合でも、障害や回復のたびに、時間の経過とともにメモリがリークします。ここでも、Apacheのメモリプールの使用のように、これを最小限に抑える方法がある程度あります。しかし、これは、スレッドが使用していた可能性のあるサードパーティのlibによって割り当てられた可能性のあるメモリを保護しません。
また、一部の人々が指摘しているように、同期プリミティブを理解することは、おそらく実際に正しく理解するのが最も難しいことです。この問題自体-すべてのコードに対して一般的なロジックを正しくするだけ-は大きな頭痛の種になる可能性があります。神秘的なデッドロックは、最も奇妙なときに発生する傾向があり、プログラムが本番環境で実行されるまで発生しないこともあるため、デバッグはさらに困難になります。これに加えて、同期プリミティブは多くの場合、プラットフォーム(WindowsとPOSIX)によって大きく異なり、多くの場合、デバッグがより困難になるだけでなく、いつでも競合状態(起動/初期化、ランタイム、シャットダウン)が発生する可能性があります。スレッドを使ったプログラミングは、初心者にとっては本当に慈悲がほとんどありません。そして、専門家であっても、スレッド化自体の知識が一般的に複雑さを最小限に抑えられないという理由だけで、まだ慈悲はほとんどありません。スレッド化されたコードの各行は、プログラムの全体的な複雑さを指数関数的に悪化させ、隠れたデッドロックや奇妙な競合状態がいつでも表面化する確率を高めるように見える場合があります。また、これらをフェレットするためのテストケースを作成することも非常に難しい場合があります。
これが、ApacheやPostgreSQLなどの一部のプロジェクトが大部分がプロセスベースである理由です。 PostgreSQLは、すべてのバックエンドスレッドを個別のプロセスで実行します。もちろん、これでも同期や競合状態の問題は軽減されませんが、かなりの保護が追加され、いくつかの点で状況が簡素化されます。
単一の実行スレッドを実行する複数のプロセスは、単一のプロセスで実行される複数のスレッドよりもはるかに優れています。また、AMQP(RabbitMQ、Qpidなど)やZeroMQのような新しいピアツーピアコードの多くが登場したことで、スレッドをさまざまなプロセススペースやマシンやネットワークにまで分割することがはるかに簡単になり、処理が大幅に簡素化されました。しかし、それでも特効薬ではありません。対処する複雑さはまだあります。変数の一部をプロセス空間からネットワークに移動するだけです。
結論としては、スレッドのドメインに入るという決定は簡単なものではないということです。その領域に足を踏み入れると、ほぼ瞬時にすべてがより複雑になり、まったく新しい種類の問題があなたの人生に入ります。楽しくてかっこいいかもしれませんが、それは原子力のようなものです。私は何年も前に臨界トレーニングのクラスを受講したことを覚えており、第二次世界大戦中の研究室でプルトニウムを扱ったロスアラモスの科学者の写真を見せてくれました。多くの人は、曝露のイベントに対してほとんどまたはまったく予防策を講じていませんでした。まばたきの中で、1回の明るく痛みのない閃光では、すべてが終わりました。数日後、彼らは亡くなりました。リチャードファインマンは、これを「 ドラゴンの尻をくすぐる 」と呼びました。それは、スレッドをいじるのが(少なくとも私にとっては)どんなものかということです。それは最初はかなり無害に思え、そしてあなたが噛まれた時までに、物事がどれだけ速く酸っぱくなったかで頭を掻いた。しかし、少なくともスレッドはあなたを殺しません。
まず、シングルスレッドアプリケーションでは、マルチコアCPUやハイパースレッディングを利用することはできません。ただし、シングルコアであっても、マルチスレッドを実行するシングルスレッドCPUには利点があります。
代替案とそれがあなたを幸せにするかどうかを検討してください。同時に実行する必要がある複数のタスクがあるとします。たとえば、2つの異なるシステムと通信し続ける必要があります。マルチスレッドなしでこれをどのように行うのですか?おそらく、独自のスケジューラを作成して、実行する必要のあるさまざまなタスクを呼び出せるようにします。つまり、タスクをいくつかの部分に分割する必要があります。おそらく、いくつかのリアルタイムの制約を満たす必要があります。部品が時間をかけすぎないようにする必要があります。そうしないと、他のタスクでタイマーが期限切れになります。これにより、タスクの分割がより困難になります。自分で管理する必要があるタスクが多いほど、実行する必要がある分割が多くなり、すべての制約を満たすためにスケジューラが複雑になります。
あなたが複数のスレッドを持っているとき、人生はより簡単になります。プリエンプティブスケジューラは、いつでもスレッドを停止し、その状態を保持し、別のスレッドを再開(開始)できます。スレッドが順番になると再起動します。利点:スケジューラの作成の複雑さはすでに行われており、タスクを分割する必要はありません。また、スケジューラーは、あなた自身が気付いていないプロセス/スレッドを管理することができます。また、スレッドが何もする必要がない場合(あるイベントを待機している場合)は、CPUサイクルを消費しません。これは、ダウンシングルスレッドスケジューラを作成する場合、それほど簡単には達成できません。 (何かを寝かせるのはそれほど難しいことではありませんが、どのように起きますか?)
マルチスレッド開発の欠点は、並行性の問題、ロック戦略などについて理解する必要があることです。エラーのないマルチスレッドコードの開発は非常に困難です。また、デバッグはさらに難しくなる可能性があります。
複数のスレッドを使用することによってのみ達成できるものはありますか?
はい。 1つのスレッドで複数のCPUまたはCPUコアでコードを実行することはできません。
複数のCPU /コアがなくても、スレッドは、サーバー上でのクライアント処理など、概念的に並行して実行されるコードを単純化できますが、スレッドなしで同じことを実行できます。
スレッドは、速度だけでなく同時実行性も重要です。
@Peterが提案するバッチアプリケーションではなく、WPFのようなGUIツールキットを使用している場合、1つのスレッドでユーザーやビジネスロジックを操作するにはどうすればよいでしょうか。
また、Webサーバーを構築しているとします。 (他のプロセスがない場合)1つのスレッドだけで複数のユーザーに同時にサービスを提供するにはどうすればよいですか?
1つのスレッドだけでは不十分なシナリオは数多くあります。これが、50以上のコアと数百のスレッドを備えたIntel MICプロセッサなどの最近の進歩が起こっている理由です。
はい、並列および並行プログラミングは困難です。しかし必要です。
マルチスレッディングにより、長い処理操作中もGUIインターフェースが応答可能になります。マルチスレッドを使用しないと、長いプロセスが実行されている間、ユーザーはロックされたフォームを見て止まってしまうでしょう。
マルチスレッドコードは、プログラムロジックをデッドロックさせ、シングルスレッドができない方法で古いデータにアクセスする可能性があります。
スレッドは、平均的なプログラマーがデバッグすると期待できる何かから曖昧なバグを取り、それをレルムに移動することができます。警告プログラマーがたまたま見ているだけで、ズボンを下にして同じバグをキャッチするのに必要な幸運について物語が伝えられます。正しい瞬間。
ブロッキングを処理するアプリIO他の入力(GUIまたは他の接続)にも応答する必要があるものは、シングルスレッド化できません
IO libにブロックなしで読み取ることができる量を確認するためのチェックメソッドの追加はこれを助けることができますが、多くのライブラリはこれについて完全な保証をしません
良い答えはたくさんありますが、私がそうであるようにフレーズがよくわかりません-多分これはそれを見る別の方法を提供します:
スレッドは、オブジェクト、アクター、またはforループのようなプログラミングの単純化です(はい、if/gotoで実装できるループで実装するものはすべて)。
スレッドがなければ、状態エンジンを実装するだけです。私は何度もこれを実行しなければなりませんでした(初めて実行したときは、聞いたこともありませんでした。 "State"変数によって制御される大きなswitchステートメントを作成しただけです)。状態マシンはまだかなり一般的ですが、煩わしい場合があります。スレッドを使用すると、ボイラープレートの巨大なチャンクが消えます。
また、言語のランタイム実行をマルチCPUに適したチャンクに分割するのを容易にします(アクターもそうだと思います)。
Javaは、OSがスレッド化をサポートしていないシステムに「グリーン」スレッドを提供します。この場合、それらがプログラミングの抽象概念にすぎないことは明らかです。
まず、スレッドは2つ以上の処理を同時に実行できます(コアが複数ある場合)。複数のプロセスでこれを行うこともできますが、一部のタスクは複数のプロセスにうまく分散しません。
また、一部のタスクには、簡単に回避できないスペースが含まれています。たとえば、ディスク上のファイルからデータを読み取り、プロセスで同時に何かを実行させることは困難です。タスクでディスクから大量のデータを読み取る必要がある場合、プロセスは何をしてもディスクを待機するために多くの時間を費やします。
第2に、スレッドを使用すると、パフォーマンスが重要ではない大量のコードを最適化する必要がなくなります。スレッドが1つしかない場合は、すべてのコードがパフォーマンスにとって重要です。それがブロックすると、沈没します-そのプロセスによって実行されるタスクは、前進することができません。スレッドでは、ブロックはそのスレッドにのみ影響し、他のスレッドが一緒になって、そのプロセスで実行する必要があるタスクを処理できます。
良い例は、まれに実行されるエラー処理コードです。タスクで非常にまれなエラーが発生し、そのエラーを処理するコードがメモリにページングする必要があるとします。ディスクがビジーで、プロセスのスレッドが1つだけの場合、そのエラーを処理するコードをメモリに読み込むことができるまで、前進することはできません。これは、バースト性のある応答を引き起こす可能性があります。
もう1つの例は、データベースの検索を行う必要がほとんどない場合です。データベースからの応答を待つと、コードに大きな遅延が発生します。しかし、これらのルックアップを実行する必要があることは非常にまれであるため、このすべてのコードを非同期にする問題に行きたくありません。この作業を行うためのスレッドを使用すると、両方の長所を利用できます。この作業を行うためのスレッドは、本来あるべきようにパフォーマンスを重要ではありません。
OSはタイムスライシングの概念を使用しており、各スレッドは実行する時間を取得してからプリエンプトされます。そのようなアプローチは、現在のところスレッドを置き換えることができますが、すべてのアプリケーションで独自のスケジューラーを作成するのはやり過ぎです。さらに、I/Oデバイスなどを使用する必要があります。また、ハードウェア側からのサポートが必要になるため、スケジューラを実行するために割り込みを発生させることができます。基本的には、毎回新しいOSを作成することになります。
一般に、スレッド化は、スレッドがI/Oを待機している場合、またはスリープしている場合にパフォーマンスを向上させることができます。また、長いタスクを実行しながら、応答性の高いインターフェイスを作成し、プロセスを停止することもできます。また、スレッド化により、真のマルチコアCPUの機能が向上します。