web-dev-qa-db-ja.com

単純なプログラム用のライブラリを作成する場合、スレッドセーフの欠如から他の人を保護するために何をする必要がありますか?

単純なプログラムのライブラリを作成する場合、スレッドセーフにする方が費用効果が高いですか、それともマルチスレッドプログラムとASSERT()でのプログラムの使用を検出する方法、または(コンパイル時またはリンク時に)それを決定する方法がありますか?問題が発生する可能性があります。

この質問に関連するヘルプは、スレッドセーフに関する潜在的な問題を見つけるための自動化されたツールのサポート、それを強制するプログラミング言語機能、

2
DeveloperDon

スレッドセーフにするためのコスト(開発時間と労力の観点から)に依存します。考慮すべき事項は次のとおりです。

  • ライブラリスレッドを安全にすることは、特にJavaのsynchronizedキーワードやC#のlockキーワードなど、効率や速度が重要な低レベルのスレッドプリミティブを処理する場合、困難な場合があります。
  • 一部の環境では、C#4.5の await and async キーワードのような高レベルの抽象化が提供されます。可能な場合はそれらを使用してください。
  • .Netの System.Collections.Concurrent 名前空間にあるようなスレッドセーフなライブラリクラスを使用します。
  • スレッドセーフは、コードの移植性に影響を与える可能性があります。たとえば、 pthread ミューテックスを使用するCコードは、追加のライブラリサポートがないとWindowsでは機能しません。

また、ライブラリがマルチスレッド環境で使用される可能性と、使用される言語の規則によっても異なります。たとえば、Microsoftのオリジナル クラスライブラリの設計ガイドラインでは、非静的メンバーはスレッドセーフである必要はないと述べられています

いずれにせよ、最善のアプローチは、スレッドセーフであるものとそうでないものを明確に文書化することです

1
akton

ですから、これは図書館を利用している人々に警告するための投票だと思います。

はい、それはまさに私の卑劣なコメントでした。その費用を追加する正当な理由がない限り、それをしないでください。スレッドセーフは高価、設計者、開発者、テスター、そしてそれを必要としないユーザーにとってです。その費用を吸収する非常に正当な理由がない限り、あなたは時期尚早の最適化の罪を犯します。

  • 正当な理由:あなたは、単一のログ項目がログファイル内の多くの行で構成され、ロガーが複数のスレッドから呼び出される可能性があるロギングライブラリに取り組んでいる開発者です。スレッドセーフの必要性は要件の中にあり、それらの要件は理にかなっています。
  • 悪い理由:ロギングライブラリでうまくやった後、あなたはプロジェクトリーダーになり、とりわけ乱数ライブラリを開発します。ここでの運転要件は、数値の正確さ、使いやすさ、速度です。プロジェクトリーダーであるあなたは、ロギングライブラリプロジェクトでスレッドセーフを確保することがとても楽しかったという理由だけで、要件にスレッドセーフを追加することにしました。あなたは私のスピードの必要性を吹き飛ばしました。モンテカルロの夜間実行中にPRNGを何兆回も呼び出す可能性があります。ライブラリは、組み込みのRand()関数とほぼ同じくらい便利です。

スレッドセーフは、それが理にかなっていて、それが必要であるために組み込まれています。それでも、どのように進めるかについて慎重に考える必要があります。

13
David Hammen

このようなもの(C#)がうまくいくかもしれません...

public class MyNonThreadSafeClass()
{
   public MyNonThreadSafeClass()
   {
       currentThreadId = Thread.CurrentThread.ManagedThreadId;
   }

   private readonly int currentThreadId;

   public void SomeNonThreadSafeMethod()
   {
      AssertSameThread();

      //do your thing   
   }

   private void AssertSameThread()
   {
      if(currentThreadId != Thread.CurrentThread.ManagedThreadId)
         throw new InvalidOperationException("Method must be called on the same thread that created the containing object");
   }
}

これにより、オブジェクトインスタンスを作成したスレッドのみがオブジェクトインスタンスを操作できるようになります。これのバリエーションにより、1つのメソッドが同時に異なるスレッドによって同時に呼び出されないようにする(ただし、1つのスレッドでインスタンスを作成し、それを別のスレッドに渡して処理できるようにする)か、作成スレッドにメッセージポンプが必要です。 (したがって、UIスレッドであるため、veryアプリの「メイン」スレッドである可能性があります)。

ただし、全体として、ライブラリがプロセスの並列スレッドによって使用されることを期待し、それをサポートしたい場合は、スレッドセーフにします。それほど難しいことではありません。モニター、ミューテックス、またはその他のロックを使用して共有状態を保護し、インスタンススコープの状態に依存しない「純粋関数」を優先します。

2
KeithS

私が提案する最大のことは、クラスは、それを不適切に使用する呼び出し元との契約を無効と見なす必要があることですが、ある呼び出し元による不適切な使用は、他の呼び出し元との契約の有効性に影響を与えないはずです。

たとえば、クラスが「Popsicle Immunity」を実装し、IsFrozenがtrueを返した場合、特定のプロパティの後続のすべての観測で同じ結果が得られると約束するとします。さらに、あるスレッドがインスタンスの「フリーズ」メソッドを呼び出し、別のスレッドがそのインスタンスを変更しているとします。オブジェクトを変更したり、Freezeを呼び出したりするスレッドは、オブジェクトが何をするかについて正当な期待を持っていませんが、IsFrozenを呼び出し、trueを返すことを確認し、他のいくつかを監視する他の場所のコードオブジェクトのプロパティは、後者のプロパティが決して変更されないことを期待する権利があります。前者のメソッドがオブジェクトを悪用したという事実は、不変であると主張するオブジェクトを期待する後者のコードの権利を弱めるべきではありません。

0
supercat