長年にわたり、研究者は、並行プログラミングの欠陥を研究するために時間とエネルギーを費やしてきました。初期の作品の多くは、この章にも言及する前に、この章では、綿密な調査[C + 71]になり、デッドロックについてです。最近の研究では、一般的な並行処理の欠陥のいくつかの他のタイプ(すなわち、非デッドロックの欠陥)に焦点を当てています。この章では、我々は簡単に、より良い問題に注意を払うことを理解するために、同時実行の問題のいくつかの例を理解するだろう。したがって、この章の重要な問題は次のとおりです。
重要な問題:一般的な並行性の欠陥に対処する方法
同時実行の欠陥は、一般的なパターンがたくさんあります。これらのパターンを理解することは、右のプログラムの最初のステップを堅牢書くことです。
欠陥のどのような種類の32.1を持っています
最初の、そして最も明白な問題は、複雑な並列プログラムでは、欠陥の種類は、それを持っていますか?一般的に、これは難しい質問ですが、幸い他の人が関わる仕事をしてきました。具体的には、Luら[L + 08]が実際に欠陥の種類を理解するために、同時に一般的なアプリケーションの数の詳細な分析、。
MySQLの(一般的なデータベース管理システム)は、Apache(知られているWebサーバ)、Mozillaの(有名なWebブラウザ)とOpenOfficeの(のMicrosoft Officeスイートのオープンソース版):研究は、4つの重要なオープンソースアプリケーションに焦点を当てました。固定されているいくつかのコードライブラリを調べることによって、同時実行の欠陥解析、開発者の仕事は、定量化可能な欠陥の研究者になります。これらの結果を理解することは、私たちは、同時実行性の問題の実際の型で登場し、成熟したコードベースに理解するのに役立ちます。
表32.1は呂あると彼の同僚の研究は結論付けています。関連した大部分の非デッドロック(74)、105の欠陥の合計を見ることができるように、残りの欠陥31がデッドロックです。また、アプリケーションごとの欠陥の数、OpenOfficeのわずか8、約60ながらMozillaを見ることができます。
統計的欠陥の表32.1最新のアプリケーション
アプリケーション名 | 使用 | 非デッドロック | デッドロック |
---|---|---|---|
MySQLの | データベースサービス | 14 | 9 |
アパッチ | Webサーバー | 13 | 4 |
Mozillaの | ウェブブラウザ | 41 | 16 |
OpenOfficeの | Officeスイート | 6 | 2 |
トータル | 74 | 31 |
欠陥のこれらの2つのタイプの現在の深さ分析。第一種良品デッドロックのために、一例として、私たちは研究を議論します。第二のタイプのデッドロックの欠陥のために、我々は、人々が予防について議論を回避し、デッドロックに行った作業の多くを扱います。
32.2非デッドロック不良
呂の研究は、非デッドロックが同時実行の問題の大半を占めていることを示しています。それが起こったのかどちらでしょうか?私たちはどのように修正すればよいですか?アトミック(アトミック違反)欠陥の違反と誤った順序(順序違反)の欠陥に:私たちは今の2に焦点を当てています。
原子欠陥の違反
質問の第一のタイプは、アトミックの違反と呼ばれています。これは、MySQLの例が表示されています。問題は、自分でどこにある読者は、最初に見つけることができます。
1 Thread 1::
2 if (thd->proc_info) {
3 ...
4 fputs(thd->proc_info, ...);
5 ...
6 }
7
8 Thread 2::
9 thd->proc_info = NULL;
この例では、2つのスレッドがTHD構造proc_infoメンバーにアクセスする必要があります。最初のスレッドのチェックは、その値を印刷し、非空proc_info;第二のスレッドはブランクとして提供されます。明らかに、スレッドは最初の検査の後、fputs()コールが第二のスレッドポインタがヌルに設定される前に中断されたとき、NULLポインタ参照がベンの崩壊にプログラムを引き起こすので、最初のスレッドは、実行を再開するとき。
。Luらによれば、原子性違反のより正式な定義は「メモリの数に違反してアクセスシリアルに期待(原子意図、すなわち、コードセグメントが、実行中及びアトミック性を強制しなかった)ことができます」 。私たちの例では、非空proc_info検査とfputs()の仮定が成り立たないとき、問題のコードは、印刷proc_info架空の原子を呼び出します。
この問題を修正し、非常に単純な(常にではないが)通常です。あなたはそれを修正する方法を考えることができますか?
このシナリオでは、私たちはフィールドproc_info各スレッドへのアクセスは、ロック(proc_info_lock)を所持していることを保証するために、共有変数へのアクセスをロックします。もちろん、このような構造にアクセスするには、他のすべてのコードは、最初にロックを取得する必要があります。
1 pthread_mutex_t proc_info_lock = PTHREAD_MUTEX_INITIALIZER;
2
3 Thread 1::
4 pthread_mutex_lock(&proc_info_lock);
5 if (thd->proc_info) {
6 ...
7 fputs(thd->proc_info, ...);
8 ...
9 }
10 pthread_mutex_unlock(&proc_info_lock);
11
12 Thread 2::
13 pthread_mutex_lock(&proc_info_lock);
14 thd->proc_info = NULL;
15 pthread_mutex_unlock(&proc_info_lock);
順番に違反して欠陥
別の一般的な非デッドロックLuらは、注文(注文違反)に違反して呼び出されます。以下は簡単な例です。あなたは不良品下記の理由コードを把握することができます場合にも、参照してください。
1 Thread 1::
2 void init() {
3 ...
4 mThread = PR_CreateThread(mMain, ...);
5 ...
6 }
7
8 Thread 2::
9 void mMain(...) {
10 ...
11 mState = mThread->State;
12 ...
13 }
あなたは、コードのスレッド2は、変数mThreadが(空でない)初期化されていることを前提としているようだということに気づいたかもしれません。ベン崩壊(mThread初期値が空であると仮定するので、しかし、最初のスレッド1とスレッドを実行することなく2がNULLポインタを参照することができる場合、スレッド2は、任意のメモリロケーションを読み取るとなるのでそうでなければ、より多くの奇妙な問題を生じさせる可能性が参考文献を参照のこと)。
より正式な定義のための違反がある:[L + 08]「二つのメモリは、注文のアクセスを期待(すなわち、AがBの前に実行されなければならないが、実際の操作は、この順序ではない)壊れています」。
注文を執行することにより、この欠陥を修正しましょう。先に詳細に説明するように、条件変数(状態変数)は、そのような現代の同期コードセットの追加単純かつ信頼性の高い方法です。上記の例では、このようにコードを変更することができます。
1 pthread_mutex_t mtLock = PTHREAD_MUTEX_INITIALIZER;
2 pthread_cond_t mtCond = PTHREAD_COND_INITIALIZER;
3 int mtInit = 0;
4
5 Thread 1::
6 void init() {
7 ...
8 mThread = PR_CreateThread(mMain, ...);
9
10 // signal that the thread has been created...
11 pthread_mutex_lock(&mtLock);
12 mtInit = 1;
13 pthread_cond_signal(&mtCond);
14 pthread_mutex_unlock(&mtLock);
15 ...
16 }
17
18 Thread 2::
19 void mMain(...) {
20 ...
21 // wait for the thread to be initialized...
22 pthread_mutex_lock(&mtLock);
23 while (mtInit == 0)
24 pthread_cond_wait(&mtCond, &mtLock);
25 pthread_mutex_unlock(&mtLock);
26
27 mState = mThread->State;
28 ...
29 }
この修理のコードでは、我々はロック(mtLock)を加え、条件変数(mtCond)だけでなく、変数の状態(mtInit)。初期化コードが実行されるmtInitが1に設定されます、そして、それはそれを行っているという信号を送ります。2前スレッドの実行が、信号および対応する状態変化を待っているならば、実行した後、初期化スレッド2か否かをチェックする(即ちmtInitが1に設定されている)場合には、通常の動作。我々は状態変数としてmThread自体を使用することができますが、簡潔にするために、我々はそうしなかったことに注意してください。順序は、スレッド間で非常に重要である場合には、条件変数(またはセマフォ)が問題を解決することができます。
非デッドロックの欠陥:概要
Luら。の調査、非デッドロックの大部分(97%)は、アトミックの二つの順序の違反に違反です。したがって、これらのプログラマーのエラーモードの注意深い研究は、それらを避けるために、より良いことができるはずです。非デッドロック開発のほとんどは両方で発見されたため、また、より自動化されたコードレビューツールの開発と、彼らは、これら2つのエラーを心配する必要があります。
しかし、すべての欠陥が修正するので、簡単に、私たちの例と同じではありません。いくつかの問題は、アプリケーションのより深い理解だけでなく、コードとデータ構造の大幅なリストラが必要です。詳細については、優れた(読み)Luら。'S紙をお読みください。
32.3デッドロックの欠陥
上記欠陥同時デッドロック(デッドロック)に加えて、複雑な並行システムの数に現れ一種類の古典的な問題です。スレッドがロックL1 1を保持している場合、例えば、我々は別のロックL2を待って、スレッドはロック2 L2、L1を保持しているが、ロック解除を待っている間に、デッドロックが発生します。デッドロックが発生する可能性が次のコード:
Thread 1: Thread 2:
lock(L1); lock(L2);
lock(L2); lock(L1);
このコードが実行されると、必ずしもそこにデッドロックが発生しません。スレッドがロックL1を占める場合に1、コンテキストは2スレッドに切り替わります。スレッド2つのロックL2、L1をロックしようとしています。しかし、それは2つのスレッドがお互いを待ち、デッドロックを生産しました。図32.1は、ここで、環(サイクル)がデッドロックを示しています。
図の依存関係図32.1デッドロック
この図は、問題の明確な説明を助けるべきです。プログラマの書き込みコードは、デッドロック、それに対処する方法をすべきですか?
重要な問題:デッドロックに対処する方法
我々はシステムを実装する場合、どのように回避するか、またはそれをデッドロック回復、検出できるようにするには?これは、現在のシステムでの本当の問題は何ですか?
なぜデッドロック
あなたは、上記のこのデッドロックの例を考えるかもしれない、避けることは容易です。例えば、限り、スレッド1とスレッド2とロックの同じ順序を取得するために使用され、デッドロックが発生することができません。それでは、なぜデッドロックが発生するのでしょうか?
一つの理由は、コードライブラリ、アセンブリとの間の大規模で複雑な依存関係があるということです。一例として、オペレーティングシステムに。その後、システムおよび仮想メモリファイルだけでなく、対話するために、ブロックを読み込む保存するためのメモリを申請する、仮想メモリシステムは、ディスクメモリページから読み取ることができ、ファイルシステムにアクセスする必要があります。そのため、大規模システムのロック機構の設計では、あなたは、循環依存に起因するデッドロックを避けるために注意する必要があります。
もう一つの理由は、(カプセル化)カプセル化されています。ソフトウェア開発者は、ソフトウェアの開発を容易にするため、モジュラーな方法で実装の詳細を非表示にする傾向がありました。しかし、モジュール性とロックは非常にフィットではありません。[J + 08]が、いくつかは、関連するインタフェースがデッドロックにつながる可能性がないように見えることを指摘ジュラ、。JavaのVectorクラスとのaddAll()メソッドは、例えば、私たちは、このメソッドを呼び出します。
Vector v1, v2;
v1.AddAll(v2);
内部的には、この方法は、マルチスレッドセーフを必要とし、したがって、ロックのベクトル(V1)及びパラメータ(V2)を得るために添加される必要があります。この方法は、v1のロックを与えると仮定すると、ロックはその後、V2を与えます。いくつかの他のスレッドがほぼ同時にv2.AddAll(V1)を呼び出す場合は、デッドロックが発生することがあります。
プロデュースデッドロック状態
デッドロックは、次の4つの条件[C + 71]を必要とします。
- ミューテックス:相互に排他的アクセスを通す必要なリソースのために(例えば、スレッドロックをつかみます)。
- 持ち、待つ:スレッドが(例えば、ロックを開催しています)他のリソースを待っている間に、(例えば、ロックを取得する必要があります)リソースを保持しています。
- ノンプリエンプティブ:(ロックなど)リソースのスレッドを取得するためには、プリエンプトすることはできません。
- ループ待つ:スレッド間のループがあり、ループに開催され、それぞれが追加のリソースであり、このリソースは次のスレッドのために適用することです。
これら4つの条件のいずれかが満たされていない場合は、デッドロックが発生しません。したがって、我々は最初のデッドロックを防止するための方法を見て、各ポリシーは、デッドロックの問題を解決するために、一定の条件を予防しようとします。
予防
円形待ち
おそらく、最も実用的な防止技術(もちろん、頻繁に使用されている)は、コードがサイクルを待つ必要はありませんようにすることです。最も直接的な方法は、ロックを取得する全順序(全順序)を提供することです。システムは2つのロック(L1およびL2)を持っている場合、我々は、各アプリケーションおよびL1 L2を申請する必要があり、デッドロックを回避することができます。ウェイトサイクルを避けるためにこのような厳格なため、デッドロックはありません。
もちろん、より複雑なシステムでは、だけでなく、2つのロックされているシーケンス全体が達成することは困難かもしれロック。このように、部分配列(半順序)がデッドロックを回避するための取り決めを獲得し、ロックする有用な方法である可能性があります。Linuxでマッピングするコード・メモリは、半順序ロック[T + 94]の良い例です。音符コードは、シーケンスロック単純な関係を含む10個の異なるセットの始まりを示し、そのようなi_mutexのi_mmap_mutex早く、また、早期i_mmap_mutexのswap_lockより前private_lock、mapping-前> tree_lockなどの複雑な関係を含みます。
あなたは、全体の秩序と部分順序慎重な設計を必要とし、ロック戦略を実装すると考えることができます。また、単に慣例の順序は、不注意なプログラマが簡単にデッドロックが生じ、無視することができます。最後に、整然としたロックミスが「D」の単語につながる場合であっても、コールの様々な機能との間の関係を理解するために、コードベースの深い理解を必要とする[1]
。
ヒント:ロックアドレスでロックを強制するため
ロック機能は、よりつかむとき、私たちは、デッドロックに注意を払う必要があります。例えば、関数がある:do_something(ミューテックスのT * m1と、ミューテックスのT * m2は)、関数は常にM1をつかむために最初である場合、M2、次いで場合do_somethingスレッドコール(L1、L2)、及びdo_something他のスレッドコール(L2、場合L1)、それはデッドロックを生成することができます。
この特定の問題を回避するには、ロックに応じ賢いプログラマは順次ロックとしてアドレスを取得しました。ローからハイへの順序でアドレス、またはロックに従って降順、do_something()関数に関係なく、パラメータが渡されている順序の保証することができ、機能が固定された順序でロックされます。具体的なコードは次のよう:
if (m1 > m2) { // grab locks in high-to-low address order
pthread_mutex_lock(m1);
pthread_mutex_lock(m2);
} else {
pthread_mutex_lock(m2);
pthread_mutex_lock(m1);
}
// Code assumes that m1 != m2 (it is not the same lock)
簡単な技術により複数のロックを取得する場合、あなたはそのデッドロックシンプルかつ効果的な実施を確保することができます。
保留と待ちます
ホールドをデッドロックと条件は、原子グラブでロックを回避することができます待ちます。実際には、次のコードで実装することができます。
1 lock(prevention);
2 lock(L1);
3 lock(L2);
4 ...
5 unlock(prevention);
予防をつかむためにロックした後に、コードロックに略奪の過程にあり、従って、デッドロックを回避し、全く不時スイッチングスレッドがないことを保証します。もちろん、これは最初のグローバル防止ロックをつかんで、いつでもプリエンプションロックで任意のスレッドが必要です。スレッドがロック防止をつかんでいるので、例えば、L1 L2を取得し、ロックする異なる順序で、別のスレッドならば、何ら問題は生じません。
何らかの理由で、このプログラムでも問題である、ということに注意してください。前と同じように、それは、パッケージには適用されません。このプログラムは、我々はこれらのロックをつかむためにロックをつかみ、事前に正確に知る必要があるため。本当に必要なときにすべてのロックが(同時に)、しかし、それは同時性を減らすことがないので、事前につかむこと。
ノンプリエンプティブ
ロック解除を呼び出す前に、ロックが別のロックを保持しながら、私たちは、ロックを待っているので、ロック操作は、多くの場合、トラブルにつながるよりつかむよりも、占有されると考えられています。多くのスレッドライブラリは、このような状況を回避するために、より柔軟なインターフェースを提供します。具体的には、のtryLock()関数は、ロックを取得しようとする、または-1を返し、ロックが占有されていることを示します。後で再度試すことができます。
このインタフェースは、ロック方法のデッドロックを達成するために使用することができます。
1 top:
2 lock(L1);
3 if (trylock(L2) == -1) {
4 unlock(L1);
5 goto top;
6 }
なお、別のスレッドが同じロック方法を使用してもよいが、異なるロック順序(L2及びLL)と、プログラムは依然としてデッドロックしないであろう。ライブロック(ライブロック):しかし、それは新たな問題につながります。そこ2つのスレッドがこのシーケンスを繰り返す必要がありますし、同時にロックをつかむことができませんでした。この場合、システムは、(それゆえではなく、デッドロック)このコードを実行されていますが、進捗状況、その名のライブロックを持っていません。溶液がライブロック:例えば、周期、第1のランダム待機時間の終了時に、繰り返さスレッド間の相互干渉を低減することができる動作を繰り返します。
この最後のポイントプログラムでは:使用のtryLockメソッドは、いくつかの問題かもしれません。最初のパッケージはまだ問題がある:特定のロックは、関数内にパッケージされている場合は、達成するために、この困難の最初に戻ってジャンプします。コードはいくつかのリソースの真ん中に入った場合、当社はこれらのリソースを解放できることを確認する必要があります。ラッシュL2が失敗し、最初に戻る前に、あなたがメモリを解放する必要がある場合たとえば、グラブL1の後、我々のコードは、いくつかのメモリを割り当てられました。もちろん、いくつかのシナリオでは(例えば、前述のJavaベクター法)、この方法は非常に有効です。
排他的
最後に、予防は完全に相互に排他的でないようにすることです。一般的に言って、コードのクリティカルセクションがあるだろう、相互に排他的に回避することは困難です。だから我々はそれがどのようにすればよいですか?
ハーリーはノーウェイト(待つのない)データ構造[H91]の様々なデザインのアイデアを提案しました。発想は単純です:強力なハードウェア命令を通じて、私たちはロックを必要としないデータ構造を構築することができます。
簡単な例として、我々はコンペア・アンド・スワップ(比較交換)命令があると、それは次のことを行うために、ハードウェアによってアトミック命令を提供するための方法は次のとおりです。
1 int CompareAndSwap(int *address, int expected, int new) {
2 if (*address == expected) {
3 *address = new;
4 return 1; // success
5 }
6 return 0; // failure
7 }
我々は数を増やすために特定の値にアトミックにするとします。私たちは、これを達成することができます:
1 void AtomicIncrement(int *value, int amount) {
2 do {
3 int old = *value;
4 } while (CompareAndSwap(value, old, old + amount) == 0);
5 }
ロックを取得せずに、値を更新した後、これらの操作のロックを解除し、我々はコンペア・アンド・スワップ命令を使用して、新しい値に更新された値の試行を繰り返しました。このロックモードは使用されませんので、(人生のロックを生成する可能性が高い)デッドロックはありません。
のは、より複雑な例を考えてみましょう:リストを挿入します。これは、要素がリストの先頭に挿入されたコードです。
1 void insert(int value) {
2 node_t *n = malloc(sizeof(node_t));
3 assert(n != NULL);
4 n->value = value;
5 n->next = head;
6 head = n;
7 }
複数のスレッドでこのコードは、同時に呼び出すと、クリティカルエリア(あなたが原因を把握できるかどうか)が存在します。もちろん、我々は、関連するコードロックを与えることによって、この問題を解決することができます。
1 void insert(int value) {
2 node_t *n = malloc(sizeof(node_t));
3 assert(n != NULL);
4 n->value = value;
5 lock(listlock); // begin critical section
6 n->next = head;
7 head = n;
8 unlock(listlock); // end of critical section
9 }
上記のシナリオ、我々は伝統的なロックを使用する[2]
。ここでは、コンペア・アンド・スワップ命令に挿入操作を実現しようとする(比較交換)。1つの可能な実装は、次のとおりです。
1 void insert(int value) {
2 node_t *n = malloc(sizeof(node_t));
3 assert(n != NULL);
4 n->value = value;
5 do {
6 n->next = head;
7 } while (CompareAndSwap(&head, n->next, n) == 0);
8 }
その後、コード、現在のリストの先頭を指す最初の次のポインタ(頭)、およびリストの先頭に新しいノードを交換してみてください。しかし、この時間は、他のスレッドが正常に新しいヘッドによれば、このスレッド再試行値が得られ、交換が失敗するヘッドの値を変更しました。
もちろん、唯一の挿入操作は、他の仕事を見つけるために、削除する必要があり、完全なリストを達成するのに十分ではありません。あなたが興味を持っている場合は、豊富な文献への同期アクセスを待たずに行くことができます。
デッドロックを回避するためにスケジュールすることで、
予防をデッドロックに加えて、シーンのいくつかは、デッドロック回避(回避)のために、より適しています。私たちは、その後のスケジューリングはデッドロックを避けることができるように、ロックの操作で別のスレッドへの需要などのグローバル情報を、理解する必要があります。
例えば、我々は二つのプロセッサ上で4つのスレッドをスケジュールする必要があるとします。さらに、我々は知っていると仮定し、スレッド1(T1)は、および必要ロックがL1、L2、T2は、つかむために必要とL1 L2、T3のみL2は、T4がロックする必要はありません。我々は32.2スレッドロックの必要性を表現するために、テーブルを使用します。
表32.2スレッドがロックする必要があります
T1 | T2 | T3 | T4 | |
---|---|---|---|---|
L1 | はい | はい | いいえ | いいえ |
L2 | はい | はい | はい | いいえ |
限り、T1とT2が同時に動作していないとして、よりインテリジェントな方法のスケジュール、デッドロックが発生しません。ここでは、この方法は以下のとおりです。
请注意,T3和T1重叠,或者和T2重叠都是可以的。虽然T3会抢占锁L2,但是由于它只用到一把锁,和其他线程并发执行都不会产生死锁。
我们再来看另一个竞争更多的例子。在这个例子中,对同样的资源(又是锁L1和L2)有更多的竞争。锁和线程的竞争如表32.3所示。
表32.3 锁和线程的竞争
T1 | T2 | T3 | T4 | |
---|---|---|---|---|
L1 | yes | yes | yes | no |
L2 | yes | yes | yes | no |
特别是,线程T1、T2和T3执行过程中,都需要持有锁L1和L2。下面是一种不会产生死锁的可行方案:
你可以看到,T1、T2和T3运行在同一个处理器上,这种保守的静态方案会明显增加完成任务的总时间。尽管有可能并发运行这些任务,但为了避免死锁,我们没有这样做,付出了性能的代价。
Dijkstra提出的银行家算法[D64]是一种类似的著名解决方案,文献中也描述了其他类似的方案。遗憾的是,这些方案的适用场景很局限。例如,在嵌入式系统中,你知道所有任务以及它们需要的锁。另外,和上文的第二个例子一样,这种方法会限制并发。因此,通过调度来避免死锁不是广泛使用的通用方案。
检查和恢复
最后一种常用的策略就是允许死锁偶尔发生,检查到死锁时再采取行动。举个例子,如果一个操作系统一年死机一次,你会重启系统,然后愉快地(或者生气地)继续工作。如果死锁很少见,这种不是办法的办法也是很实用的。
提示:不要总是完美(TOM WEST定律)
Tom West是经典的计算机行业小说《Soul of a New Machine》[K81]的主人公,有一句很棒的工程格言:“不是所有值得做的事情都值得做好”。如果坏事很少发生,并且造成的影响很小,那么我们不应该去花费大量的精力去预防它。当然,如果你在制造航天飞机,事故会导致航天飞机爆炸,那么你应该忽略这个建议。
很多数据库系统使用了死锁检测和恢复技术。死锁检测器会定期运行,通过构建资源图来检查循环。当循环(死锁)发生时,系统需要重启。如果还需要更复杂的数据结构相关的修复,那么需要人工参与。
读者可以在其他地方找到更多的关于数据库并发、死锁和相关问题的资料[B+87,K87]。阅读这些著作,当然最好可以通过学习数据库的课程,深入地了解这一有趣而且丰富的主题。
32.4 小结
在本章中,我们学习了并发编程中出现的缺陷的类型。第一种是非常常见的,非死锁缺陷,通常也很容易修复。这种问题包括:违法原子性,即应该一起执行的指令序列没有一起执行;违反顺序,即两个线程所需的顺序没有强制保证。
同时,我们简要地讨论了死锁:为何会发生,以及如何处理。这个问题几乎和并发一样古老,已经有成百上千的相关论文了。实践中是自行设计抢锁的顺序,从而避免死锁发生。无等待的方案也很有希望,在一些通用库和系统中,包括Linux,都已经有了一些无等待的实现。然而,这种方案不够通用,并且设计一个新的无等待的数据结构极其复杂,以至于不够实用。也许,最好的解决方案是开发一种新的并发编程模型:在类似MapReduce(来自Google)[GD02]这样的系统中,程序员可以完成一些类型的并行计算,无须任何锁。锁必然带来各种困难,也许我们应该尽可能地避免使用锁,除非确信必须使用。
本文摘自刚刚上架的《操作系统导论》(Operating Systems)
作者:[美] 雷姆兹·H.阿帕希杜塞尔( Remzi H. Arpaci-Dusseau), [美]安德莉亚·C.阿帕希杜塞尔(Andrea C. Arpaci-Dusseau)
译者:王海鹏
- 美国知名操作系统教材
- 紧紧围绕操作系统的三大主题元素:虚拟化 并发和持久性进行讲解
- 豆瓣原版评分9.7
仮想化周りの本は、3つの主要な概念は、並行して開始した持続的、(スケジューリング、仮想メモリ管理、ディスクとI / Oサブシステム、ファイルシステムを含む)最近のシステムのすべての主要なコンポーネントを備えています。この本は、すなわち、仮想化、並行処理、および関連するコンテンツの持続性について、3つの部分に分かれ50章を、持っています。導入されたテーマの形での対話の概念の導入が、ユーモアの鋭い言語であり、読者は、オペレーティングシステムの仮想化、並行性、および持続性の原理を理解するために努力しています。
この本は、教師が高等教育と教育大学の学生自習の関連する専門機関を実施することが適当であり、また、対応する練習を提供し、包括的で、本当のを実行するためのコード(ない擬似コード)を与えます。
この本は、次の機能があります
●著名なテーマは、オペレーティングシステムの3つの主要なテーマに焦点を当てて-仮想化、並行処理、および持続。
●背景の導入の対話は、質問をして、実践的な練習に触発原則を、説明します。
●、読者の知識を広げる楽しさを高めるために、「相補的」と「ヒント」の数が含まれています。
●読者のより詳細な、オペレーティングシステムの完全な理解を可能にする、実際のコードの代わりに擬似コードを使用して。
●ハンズオン練習に読者を奨励するために多くの仕事の学習、シミュレーションやプロジェクトを提供しています。
●エイズを教える教師のためのリソースを提供します。
この本は、次のようにエイズを教える教師のためのリソースを提供しています。
- PPTの教育と講義ノート。
- 試験の質問と回答を示唆しました。
- 討論のための質問と割り当て。
- プロジェクトの説明と指導。
- あなたは、教師なサポートリソースを教えたい場合は、[email protected]アプリケーションに電子メールを送ってください。