マルチスレッドのバグに悩まされる


26

私が管理している新しいチームでは、コードの大部分はプラットフォーム、TCPソケット、およびhttpネットワークコードです。すべてのC ++。その大部分は、チームを去った他の開発者からのものです。チームの現在の開発者は非常に賢いですが、ほとんどの場合、経験的には後輩です。

私たちの最大の問題:マルチスレッドの同時実行バグ。ほとんどのクラスライブラリは、いくつかのスレッドプールクラスを使用して非同期に作成されています。クラスライブラリのメソッドは、長時間実行されるタスクを1つのスレッドからスレッドプールにキューイングすることが多く、そのクラスのコールバックメソッドは別のスレッドで呼び出されます。その結果、誤ったスレッドの仮定に関連する多くのエッジケースバグがあります。これにより、同時実行性の問題から保護するための重要なセクションとロックを保持するだけでなく、微妙なバグが発生します。

これらの問題をさらに困難にしているのは、修正の試みがしばしば間違っていることです。チームが(またはレガシーコード自体で)試みようとしているミスには、次のようなものがあります。

よくある間違い#1-共有データをロックするだけで同時実行の問題を修正しますが、メソッドが期待される順序で呼び出されない場合に何が起こるかを忘れます。これは非常に簡単な例です:

void Foo::OnHttpRequestComplete(statuscode status)
{
    m_pBar->DoSomethingImportant(status);
}

void Foo::Shutdown()
{
    m_pBar->Cleanup();
    delete m_pBar;
    m_pBar=nullptr;
}

そのため、OnHttpNetworkRequestCompleteの実行中にShutdownを呼び出すことができるバグがあります。テスターはバグを見つけ、クラッシュダンプをキャプチャし、バグを開発者に割り当てます。彼は次のようにバグを修正します。

void Foo::OnHttpRequestComplete(statuscode status)
{
    AutoLock lock(m_cs);
    m_pBar->DoSomethingImportant(status);
}

void Foo::Shutdown()
{
    AutoLock lock(m_cs);
    m_pBar->Cleanup();
    delete m_pBar;
    m_pBar=nullptr;
}

上記の修正は、さらに微妙なエッジケースがあることに気付くまでは問題ありません。OnHttpRequestCompleteがコールバックされる前にシャットダウンが呼び出されるとどうなりますか?私のチームが持っている実際の例はさらに複雑であり、コードレビュープロセス中にエッジケースを見つけるのはさらに困難です。

よくある間違い#2-盲目的にロックを終了し、他のスレッドが終了するのを待ってからロックを再入力することで、デッドロックの問題を修正します。

よくある間違い#3-オブジェクトは参照カウントされますが、シャットダウンシーケンスはポインターを「解放」します。しかし、まだ実行中のスレッドがそのインスタンスを解放するのを待つことを忘れています。そのため、コンポーネントは完全にシャットダウンされ、その後、コールを予期しない状態のオブジェクトでスプリアスまたはレイトコールバックが呼び出されます。

他のエッジケースもありますが、一番下の行はこれです:

マルチスレッドプログラミングは、頭のいい人でも簡単です。

これらの間違いを見つけると、より適切な修正を開発するために各開発者とエラーについて話し合うことに時間を費やします。しかし、「正しい」修正には触れなければならない膨大な量のレガシコードのため、各問題の解決方法についてしばしば混乱していると思われます。

私たちはすぐに出荷するつもりであり、私たちが適用しているパッチは今後のリリースに適用されると確信しています。その後、コードベースを改善し、必要に応じてリファクタリングする時間があります。すべてを書き直す時間はありません。そして、コードの大部分はそれほど悪くはありません。しかし、スレッドの問題を完全に回避できるように、コードをリファクタリングしたいと考えています。

私が検討しているアプローチの1つはこれです。重要なプラットフォーム機能ごとに、すべてのイベントとネットワークコールバックがマーシャリングされる専用の単一スレッドを用意します。メッセージループを使用したWindowsでのCOMアパートメントスレッドに似ています。長時間のブロッキング操作はワークプールスレッドにディスパッチされる可能性がありますが、コンポーネントのスレッドで完了コールバックが呼び出されます。コンポーネントは同じスレッドを共有することさえできます。その後、スレッド内で実行されるすべてのクラスライブラリは、単一のスレッド化された世界を想定して記述できます。

その道を進む前に、マルチスレッドの問題に対処するための他の標準的な手法や設計パターンがあるかどうかにも非常に興味があります。そして、私は強調しなければなりません-ミューテックスとセマフォの基本を説明する本を超えた何か。どう思いますか?

また、リファクタリングプロセスに向けた他のアプローチにも興味があります。次のいずれかを含む:

  1. スレッド周辺のデザインパターンに関する文献または論文。ミューテックスとセマフォの紹介を超えたもの。大規模な並列処理も必要ありません。他のスレッドからの非同期イベントを正しく処理するようにオブジェクトモデルを設計する方法だけです。

  2. さまざまなコンポーネントのスレッド化を図式化する方法。これにより、ソリューションの研究と発展が容易になります。(つまり、オブジェクトおよびクラス全体のスレッドを議論するためのUML同等物)

  3. マルチスレッドコードの問題について開発チームを教育します。

  4. あなたならどうしますか?


23
問題に直面したときに、マルチスレッドを使用すると思う人もいます。現在、2つのプローブがあります
トムスクワイアズ

20
私にとってうまくいくのは、可能な限り可変を取り除くことです。新しい値を渡すために可変オブジェクトの状態が変化するのを見ると、それをリファクタリングして、変更された値を保持する新しい不変オブジェクトを渡すようにします。オブジェクトの初期化が安全に行われた場合、これによりデータの競合がないことが保証されます-非常に安心
-gnat

3
マルチスレッド地獄へようこそ。Ada、Occam、C ++で20年以上、マルチスレッド/並列プログラムを書いてきました。決して簡単なことではありません。すべてに非常に慎重な思考が必要であり、「簡単にXを実行する」と言う人はだれでも何が起こっているのか本当に理解していないバカです。がんばろう。
すぐに

2
並行性がうまく行われていることを確認したい場合は、Erlangを使用してください!実際に必要なのは、奇妙なコーナーケースが排除されるシェアードナッシングアクターモデルの形式です。
ザカリーK

3
@DeadMG私は、共有状態の同時実行は本質的に奇妙なコーナーケースになりがちであり、避けるべきだと主張します。しかし、ちょっと私はアーランに関する本を書いた
ザカリーK

回答:


27

コードには、それ以外にも重要な問題があります。手動でポインターを削除しますか?cleanup関数を呼び出しますか?うわ また、質問のコメントで正確に指摘されているように、ロックにはRAIIを使用しません。これは別の非常に重大な失敗でありDoSomethingImportant、例外がスローされたときにひどいことが起こることを保証します。

このマルチスレッドのバグが発生しているという事実は、コアの問題の兆候にすぎません。コードはスレッド化の状況において非常に悪いセマンティクスを持ち、完全に信頼性の低いツールとイディオムを使用しています。もし私があなたなら、それはもちろん、単一のスレッドで機能することに驚くでしょう。

よくある間違い#3-オブジェクトは参照カウントされますが、シャットダウンシーケンスはポインターを「解放」します。しかし、まだ実行中のスレッドがそのインスタンスを解放するのを待つことを忘れています。そのため、コンポーネントは完全にシャットダウンされ、その後、コールを予期しない状態のオブジェクトに対して、誤ったコールバックまたはレイトコールバックが呼び出されます。

参照カウントの全体的なポイントは、スレッドが既にそのインスタンスを解放していることです。そうでない場合は、スレッドにまだ参照があるため、破棄できないためです。

を使用しstd::shared_ptrます。すべてのスレッドが解放されている(とすると誰もが、それゆえ、彼らはそれへのポインタを持っていないとして、関数を呼び出すことはできません)、そしてデストラクタが呼ばれています。これは安全であることが保証されています。

次に、IntelのThread Building BlocksやMicrosoftのParallel Patterns Libraryなどの実際のスレッド化ライブラリを使用します。独自の記述は時間がかかり、信頼性が低く、コードには必要のないスレッドの詳細がいっぱいです。独自のロックを行うことは、独自のメモリ管理を行うことと同じくらい悪いです。彼らはすでに、あなたの使用のために正しく機能する多くの汎用の非常に便利なスレッド化イディオムを実装しています。


これは大丈夫な答えですが、私が探していた方向ではありません。単純化のために書かれたサンプルコードの評価に時間がかかりすぎるためです(製品の実際のコードを反映していません)。しかし、私はあなたが行った1つのコメント、「信頼できないツール」に興味があります。信頼できないツールとは何ですか?どのツールをお勧めしますか?
koncurrency

5
@koncurrency:信頼性の低いツールとは、手動メモリ管理や独自の同期の作成のようなもので、理論的には問題Xを解決しますが、実際には非常に悪いため、大きな間違いと、おそらく問題を解決できる唯一の方法を保証できます合理的な規模で手元にあるのは、開発者の時間の膨大で不均衡な投資です。
-DeadMG

9

他のポスターは、コアの問題を修正するために何をすべきかについてよくコメントしています。この投稿は、適切な方法ですべてをやり直すための時間を稼ぐのに十分なほどレガシーコードにパッチを当てるという、より差し迫った問題に関係しています。言い換えれば、これは物事を行うための正しい方法ではなく今のところはぐにゃぐにゃとする方法です。

重要なイベントを統合するというあなたのアイデアは良いスタートです。順序の依存関係がある場合は、単一のディスパッチスレッドを使用して、すべてのキー同期イベントを処理するようにします。スレッドセーフなメッセージキューを設定し、同時実行に敏感な操作(割り当て、クリーンアップ、コールバックなど)を現在実行している場所ではなく、代わりにそのスレッドにメッセージを送信し、操作を実行またはトリガーします。この1つのスレッドは、すべてのワークユニットの開始、停止、割り当て、およびクリーンアップを制御するという考え方です。

ディスパッチスレッドは、説明した問題を解決するのではなく、それらを1か所に統合​​するだけです。それでも、予期しない順序で発生するイベント/メッセージについて心配する必要があります。実行時間が長いイベントは、他のスレッドに送信する必要があるため、共有データの同時実行に関する問題が残っています。これを軽減する1つの方法は、参照によるデータの受け渡しを回避することです。可能な限り、ディスパッチメッセージのデータは、受信者が所有するコピーである必要があります。(これは、他の人が言及したデータを不変にするという方針に沿っています。)

このディスパッチアプローチの利点は、ディスパッチスレッド内に、少なくとも特定の操作が連続して発生していることを少なくとも知っている一種の安全な避難場所があることです。欠点は、ボトルネックと余分なCPUオーバーヘッドが発生することです。最初はこれらのことを心配しないことをお勧めします。ディスパッチスレッドにできる限り移動することで、まず正しい操作の測定値を取得することに焦点を合わせます。次に、プロファイリングを行って、CPU時間を最も多く消費しているものを確認し、正しいマルチスレッド手法を使用してディスパッチスレッドからシフトを戻します。

繰り返しますが、私が説明しているのは、物事を行う正しい方法ではありませんが、商業的な締め切りに間に合うように少しずつ適切な方法であなたを動かすプロセスです。


既存の課題を克服するための合理的で中間的な提案に対して+1。

はい、これは私が調査しているアプローチです。パフォーマンスについて良い点を挙げます。
-koncurrency

単一のディスパッチスレッドを通過するように物事を変更することは、簡単なパッチのようではなく、私にとって大規模なリファクタリングのように聞こえます。
セバスチャンレッド

8

示されているコードに基づいて、WTFの山があります。不完全に記述されたマルチスレッドアプリケーションを段階的に修正することは、不可能ではないにしても非常に困難です。大幅な手直しを行わないと、アプリケーションの信頼性が低下することを所有者に伝えます。共有オブジェクトとやり取りしているコードのすべてのビットを調べて、手直しすることに基づいて推定値を与えます。まず、検査の見積もりを伝えます。その後、リワークの見積もりを提供できます。

コードを修正するときは、間違いなく正しいようにコードを書くことを計画する必要があります。その方法がわからない場合は、そうする人を見つけてください。そうしないと、同じ場所にたどり着きます。


私の答えが支持された後、今これを読んでください。私は紹介文が大好きだと言いたかっただけです:)
back2dos

7

アプリケーションのリファクタリングに専念する時間があれば、アクターモデルをご覧になることをお勧めします(たとえば、TheronCasablancalibcppa、C ++実装のCAFを参照)。

アクターは、同時に実行され、非同期メッセージ交換のみを使用して相互に通信するオブジェクトです。そのため、スレッド管理、ミューテックス、デッドロックなどのすべての問題はアクター実装ライブラリによって処理され、オブジェクト(アクター)の動作の実装に集中できます。

  1. メッセージを受け取る
  2. 計算を実行する
  3. メッセージを送信/他のアクターを作成/殺します。

あなたのための一つのアプローチはいくつかやって可能性が読書を最初のトピックに、そしておそらく俳優モデルがあなたのコードに統合することが可能かどうかを確認するには1つのライブラリまたは2を見てみましょう。

私は、このモデル(私の簡易版)を私のプロジェクトで数か月使用しており、その堅牢性に驚いています。


1
ScalaのAkkaライブラリは、これをうまく実装したもので、子が死んだときに親のアクターを殺す方法、またはその逆を考える多くの方法です。:私はそれがC ++ではありません知っているが、一見の価値akka.io
GlenPeterson

1
@GlenPeterson:ありがとう、私はakka(現時点で最も興味深いソリューションと考えており、JavaとScalaの両方で動作します)について知っていますが、質問は特にC ++に対処しています。そうでなければ、イベントはアーランを考慮する可能性があります。Erlangでは、マルチスレッドプログラミングの頭痛の種がすべてなくなったと思います。しかし、akkaのようなフレームワークは非常に近いかもしれません。
ジョルジオ

「Erlangでは、マルチスレッドプログラミングの頭痛の種はすべてなくなったと思います。」多分これは少し誇張されていると思います。または、trueの場合、パフォーマンスが不足している可能性があります。AkkaはC ++では動作せず、複数のスレッドを管理するための最新技術のように見えることを知っています。ただし、スレッドセーフではありません。それでも、俳優間で可変状態を渡し、自分自身を足で撃つことができます。
グレンペターソン

私はアーランの専門家ではありませんが、知る限りでは各アクターは単独で実行され、不変のメッセージが交換されます。したがって、スレッドと共有された可変状態を扱う必要はまったくありません。パフォーマンスはおそらくC ++よりも低くなりますが、抽象化レベルを上げると常に発生します(実行時間は増加しますが、開発時間は減少します)。
ジョルジオ

downvoterはコメントを残して、この答えを改善する方法を提案できますか?
ジョルジオ

6

よくある間違い#1-共有データをロックするだけで同時実行の問題を修正しますが、メソッドが期待される順序で呼び出されない場合に何が起こるかを忘れます。これは非常に簡単な例です:

ここでの間違いは、「忘れる」ことではなく、「修正しない」ことです。予期しない順序で物事が発生している場合、問題があります。回避策を試みるのではなく解決する必要があります(通常、ロックを何かにスラップすることは回避策です)。

アクターモデル/メッセージングをある程度適合させ、懸念を分離するようにしてください。の役割Fooは明らかに、ある種のHTTP通信を処理することです。これを並行して行うようにシステムを設計する場合、オブジェクトのライフサイクルを処理し、それに応じて同期をアクセスする必要があるのは、上の層です。

同じ可変データ上で多数のスレッドを操作しようとするのは困難です。しかし、それはめったに必要ではありません。これを必要とするすべての一般的なケースは、すでに管理しやすい概念に抽象化されており、主要な命令型言語について何度も実装されています。使用する必要があります。


2

あなたの問題はかなりひどいですが、典​​型的なC ++の貧弱な使用です。コードレビューにより、これらの問題の一部が修正されます。30分、1組の眼球が結果の90%を獲得します(この引用はグーグル検索可能です)

#1問題ロックのデッドロックを防ぐために、厳密なロック階層があることを確認する必要があります。

Autolockをラッパーとマクロで置き換える場合、これを行うことができます。

ラッパーの背面に作成されたロックの静的なグローバルマップを保持します。マクロを使用して、ファインロック名と行番号の情報をAutolockラッパーコンストラクターに挿入します。

また、静的ドミネーターグラフも必要になります。

ここで、ロックの内側でドミネーターグラフを更新する必要があります。順序の変更が発生した場合は、エラーをアサートして中止します。

広範なテストの後、潜在的なデッドロックのほとんどを取り除くことができます。

コードは、学生の演習として残されています。

その後、問題#2はなくなります(ほとんど)

あなたのアーキテクチュアルソリューションは機能します。私は以前、ミッションおよびライフクリティカルシステムでそれを使用しました。私の考えはこれです

  • 不変オブジェクトを渡すか、渡す前にそれらのコピーを作成します。
  • パブリック変数またはゲッターを介してデータを共有しないでください。

  • 外部イベントは、1つのスレッドが処理するキューへのマルチスレッドディスパッチを介して着信します。これで、イベント処理に関するさまざまな理由を説明できます。

  • スレッドを横断するデータ変更は、スレッドセーフなキューに入り、1つのスレッドで処理されます。サブスクリプションを作成します。これで、データフローに関する理由を並べ替えることができます。

  • データが町を横断する必要がある場合は、データキューに公開します。それはそれをコピーし、それをサブスクライバーに非同期に渡します。また、プログラム内のすべてのデータ依存関係を壊します。

これは、かなり安価な俳優モデルです。ジョルジオのリンクが役立ちます。

最後に、シャットダウンオブジェクトに関する問題。

参照カウントを行っているとき、50%を解決しました。他の50%は参照カウントのコールバックです。コールバックホルダーに参照を渡します。シャットダウンコールは、refcountでゼロカウントを待機する必要があります。複雑なオブジェクトグラフを解決しません。それが実際のガベージコレクションになっています。(これは、finalize()がいつ呼び出されるか、またはいつ呼び出されるかについて約束しないというJavaの動機です。その方法でプログラミングから抜け出すためです。)


2

将来の探求者のために:アクターモデルに関する回答を補完するために、CSP(プロセスの通信)を追加します。プロセス計算のより大きなファミリーにうなずきます。CSPはアクターモデルに似ていますが、分割方法が異なります。まだたくさんのスレッドがありますが、それらは互いに具体的にではなく、特定のチャネルを介して通信し、両方のプロセスはどちらかが発生する前にそれぞれ送受信する準備ができている必要があります。CSPコードが正しいことを証明するための形式化された言語もあります。CSPの使用に移行し続けていますが、数か月間、いくつかのプロジェクトでCSPを使用しており、現在は非常に単純化されています。

ケント大学は(C ++の実装があるhttps://www.cs.kent.ac.uk/projects/ofa/c++csp/でクローン化され、https://github.com/themasterchef/cppcsp2を)。


1

スレッド周辺のデザインパターンに関する文献または論文。ミューテックスとセマフォの紹介を超えたもの。大規模な並列処理も必要ありません。オブジェクトモデルを設計して、他のスレッドからの非同期イベントを正しく処理する方法だけが必要です。

私は現在これを読んでおり、C ++であなたが得ることができるすべての問題とそれらを回避する方法を説明しています(新しいスレッドライブラリを使用しますが、グローバルな説明はあなたのケースに有効だと思います): http://www.amazon。 com / C-Concurrency-Action-Practical-Multithreading / dp / 1933988770 / ref = sr_1_1?ie = UTF8&qid = 1337934534&sr = 8-1

さまざまなコンポーネントのスレッド化を図式化する方法。これにより、ソリューションの研究と発展が容易になります。(つまり、オブジェクトおよびクラス全体のスレッドを議論するためのUML同等物)

私は個人的に簡略化されたUMLを使用し、メッセージは非同期で行われると仮定しています。また、これは「モジュール」間でも当てはまりますが、内部のモジュールについては知りたくありません。

マルチスレッドコードの問題について開発チームを教育します。

この本は役に立ちますが、エクササイズ/プロトタイピングと経験豊富なメンターはベターだと思います。

あなたならどうしますか?

並行性の問題を理解していない人がプロジェクトで作業することを完全に避けます。しかし、あなたはそれができないと思うので、あなたの特定のケースでは、チームがより教育を受けられるようにする以外に、私にはわからない。


本の提案をありがとう。拾うでしょう。
-koncurrency

スレッド化は本当に難しいです。すべてのプログラマーが挑戦するわけではありません。ビジネスの世界では、スレッドが使用されるたびに、2つのスレッドが同時に実行できないようにロックに囲まれていました。それを簡単にするために従うことができる規則がありますが、それはまだ難しいです。
グレンペターソン

@GlenPeterson Agreeed、私はより多くの経験を持っているので(この答えから)、それを管理しやすくし、データの共有を思いとどまらせるためにより良い抽象化が必要であることがわかりました。幸い、言語設計者はこれに一生懸命取り組んでいるようです。
クライム

Scalaには、特にC ++の直接の子孫であるJavaに不変性、最小限の副作用の関数型プログラミングの利点をもたらすことに本当に感銘を受けました。Java仮想マシンで実行されるため、必要なパフォーマンスが得られない場合があります。Joshua Blochの著書「Effective Java」は、可変性の最小化、気密インターフェースの作成、およびスレッドセーフティに関するものです。Javaベースですが、その80〜90%をC ++に適用できると思います。コードレビューでの可変性と共有状態(または共有状態の可変性)に疑問を呈することは、最初の良いステップです。
グレンペターソン

1

あなたはすでに問題を認め、積極的に解決策を探している途中です。ここに私がやることがあります:

  • 座って、アプリケーションのスレッドモデルを設計します。これは、次のような質問に答えるドキュメントです:どのタイプのスレッドを持っていますか?どのスレッドでどのようなことを行う必要がありますか?どのような種類の同期パターンを使用する必要がありますか?言い換えれば、マルチスレッドの問題と闘うときの「関与のルール」を記述する必要があります。
  • スレッド分析ツ​​ールを使用して、コードベースのエラーを確認します。ValgrindにはHelgrindと呼ばれるスレッドチェッカーがあり、適切な同期なしで操作されている共有状態などを見つけるのに適しています。確かに他にも優れたツールがあります。探してみてください。
  • C ++からの移行を検討してください。C ++は並行プログラムを作成する悪夢です。個人的にはErlangを選択しますが、それは好みの問題です。

8
最後のビットは間違いなく-1。OPのコードは実際のC ++ツールではなく、最も原始的なツールを使用しているように見えます。
DeadMG

2
私は同意しません。適切なC ++メカニズムとツールを使用しても、C ++の同時実行は悪夢です。そして、「検討する」という言葉を選んだことに注意してください。私はそれが現実的な代替案ではないかもしれないことを完全に理解していますが、代替案を考慮せずにC ++にとどまることは愚かなことです。
JesperE

4
@JesperE-申し訳ありませんが、ありません。C ++での同時実行は、低すぎるレベルに移行して1つにした場合にのみ悪夢です。適切なスレッド抽象化を使用すると、他の言語やランタイムよりも悪くありません。そして、適切なアプリケーション構造があれば、実際に私がこれまで見た他のものと同じくらい簡単です。
マイケルコーネ

2
私が働いているところでは、適切なアプリケーション構造があり、正しいスレッド抽象化を使用しているなどと信じています。それにも関わらず、私たちは長年にわたり、並行処理用に適切に設計された言語では表示されないバグのデバッグに数時間を費やしてきました。しかし、私はこれに反対することに同意しなければならないと感じています。
JesperE

1
@JesperE:私はあなたに同意します。Erlangモデル(Scala / Java、Ruby、および私の知る限りC ++の実装が存在するモデル)は、スレッドで直接コーディングするよりもはるかに堅牢です。
ジョルジオ

1

あなたの例を見てみましょう:Foo :: Shutdownが実行を開始するとすぐに、OnHttpRequestCompleteを呼び出して実行することはできません。それはどの実装とも関係がなく、機能しません。

また、OnHttpRequestCompleteの呼び出しの実行中(間違いなくtrue)にFoo :: Shutdownを呼び出し可能にすべきではなく、おそらくOnHttpRequestCompleteの呼び出しがまだ未解決の場合はそうではないと主張することもできます。

最初に正しいことは、ロックなどではなく、許可されているかどうかのロジックです。単純なモデルは、クラスにゼロ個以上の不完全なリクエスト、まだ呼び出されていないゼロ個以上の完了、実行中のゼロ個以上の完了があり、オブジェクトがシャットダウンしたいかどうかを示します。

Foo :: Shutdownは、完了の実行を終了し、可能な場合はシャットダウンできるポイントまで不完全な要求を実行し、それ以上の完了を開始できないようにし、さらに要求を開始できないようにします。

何をする必要があるか:正確に何をするかを示す仕様を関数に追加します。(たとえば、シャットダウンが呼び出された後にhttp要求の開始が失敗する場合があります)。そして、仕様を満たすように関数を作成します。

ロックは、共有変数の変更を制御するために、可能な限り短時間だけ使用するのが最適です。そのため、ロックによって保護されている変数「performingShutDown」を使用できます。


0

あなたならどうしますか?

正直に言うと; すぐに逃げてしまいました。

並行性の問題は厄介です。何かが数か月間完璧に機能し、その後(いくつかの特定のタイミングにより)突然、顧客の顔を爆破します。ソフトウェアとは関係のないハードウェアのグリッチではないことを確認するためです。

並行性の問題の回避は、設計段階で開始する必要があります。まず、その方法(「グローバルロック順序」、アクターモデルなど)から始めます。これは、今後のリリース後にすべてが自己破壊しないことを期待して、狂ったパニックで修正しようとするものではありません。

ここで冗談を言っているわけではないことに注意してください。あなた自身の言葉(「そのほとんどは、チームを去った他の開発者から生まれました。チームの現在の開発者は非常に賢いですが、経験の点ではほとんどが後輩です。」)提案しています。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.