ある値を受け入れ、他の値を返し、関数の外部に影響を与えず、副作用がなく、したがってスレッドセーフな関数。関数の実行方法が電力消費にどのように影響するかなどを検討したい場合、それは別の問題です。
私は、あなたが何らかの明確に定義されたプログラミング言語を実行しているチューリング完全なマシンを参照していると仮定しています。そこでは実装の詳細は無関係です。言い換えれば、選択したプログラミング言語で記述している関数が言語の範囲内で不変性を保証できるのであれば、スタックが何をしているのかは問題ではありません。高級言語でプログラミングしているときは、スタックについては考えませんし、そうする必要もありません。
これがどのように機能するかを説明するために、C#でいくつかの簡単な例を提供します。これらの例が真実であるためには、いくつかの仮定をしなければなりません。まず、コンパイラがエラーなしでC#仕様に従っていること、そして2番目に、正しいプログラムを生成すること。
文字列コレクションを受け入れ、コレクション内のすべての文字列をコンマで区切って連結した文字列を返す単純な関数が必要だとしましょう。C#での単純で単純な実装は、次のようになります。
public string ConcatenateWithCommas(ImmutableList<string> list)
{
string result = string.Empty;
bool isFirst = false;
foreach (string s in list)
{
if (isFirst)
result += s;
else
result += ", " + s;
}
return result;
}
この例は不変です。どうやってそれを知るのですか?そのためstring
、オブジェクトは不変です。ただし、実装は理想的ではありません。result
は不変であるため、ループを通るたびに新しい文字列オブジェクトを作成し、result
指す元のオブジェクトを置き換える必要があります。これは、余分な文字列をすべてクリーンアップする必要があるため、速度に悪影響を及ぼし、ガベージコレクターに圧力をかける可能性があります。
今、私がこれを行うとしましょう:
public string ConcatenateWithCommas(ImmutableList<string> list)
{
var result = new StringBuilder();
bool isFirst = false;
foreach (string s in list)
{
if (isFirst)
result.Append(s);
else
result.Append(", " + s);
}
return result.ToString();
}
string
result
可変オブジェクトに置き換えたことに注目してくださいStringBuilder
。これは最初の例よりもはるかに高速です。ループを通過するたびに新しい文字列が作成されないためです。代わりに、StringBuilderオブジェクトは単に各文字列の文字を文字のコレクションに追加し、最後にすべてを出力します。
StringBuilderは可変ですが、この関数は不変ですか?
はい、そうです。どうして?そのため、単にその呼び出しのために、この関数が呼び出されるたびに、新しいStringBuilderのが作成されます。 これで、スレッドセーフですが、可変コンポーネントを含む純粋な関数ができました。
しかし、これを実行した場合はどうなりますか?
public class Concatenate
{
private StringBuilder result = new StringBuilder();
bool isFirst = false;
public string ConcatenateWithCommas(ImmutableList<string> list)
{
foreach (string s in list)
{
if (isFirst)
result.Append(s);
else
result.Append(", " + s);
}
return result.ToString();
}
}
このメソッドはスレッドセーフですか?いいえ、そうではありません。どうして?そのため、クラスは今、私の方法が依存している状態を保持しています。 メソッドに競合状態が存在するようになりました。1つのスレッドが変更するIsFirst
可能性がありますが、別のスレッドが最初Append()
に実行する可能性があります。
なぜこのようにしたいのですか?まあ、私はスレッドがresult
順序に関係なく、またはスレッドが入ってくる順序で私の中に文字列を蓄積したいかもしれません。
とにかく、それを修正するためにlock
、メソッドの内部にステートメントを付けました。
public class Concatenate
{
private StringBuilder result = new StringBuilder();
bool isFirst = false;
private static object locker = new object();
public string AppendWithCommas(ImmutableList<string> list)
{
lock (locker)
{
foreach (string s in list)
{
if (isFirst)
result.Append(s);
else
result.Append(", " + s);
}
return result.ToString();
}
}
}
これで再びスレッドセーフになりました。
私の不変のメソッドがスレッドセーフにならない可能性がある唯一の方法は、メソッドが実装の一部を何らかの形でリークする場合です。これは起こりますか?コンパイラが正しく、プログラムが正しい場合ではありません。そのような方法でロックが必要になることはありますか?いや
並行性シナリオで実装がどのようにリークされる可能性があるかの例については、こちらを参照してください。
but everything has a side effect
-ええ、そうではありません。ある値を受け入れ、他の値を返し、関数の外部に影響を与えず、副作用がないため、スレッドセーフな関数。コンピューターが電気を使用していても構いません。必要に応じて、メモリセルに衝突する宇宙線についても説明できますが、議論は現実的なものにしましょう。関数の実行方法が電力消費にどのように影響するかなどを検討したい場合、それはスレッドセーフプログラミングとは異なる問題です。