C#では、変数のサイズが最大でnative int
(つまり、32ビットランタイム環境では4バイト、64ビット環境では8バイト)。すべての参照型とほとんどの組み込み値型を含む64ビット環境(byte
、short
、int
、long
など) 。
大きな値を設定することはアトミックではなく、メモリの一部のみが更新されるティアリングを引き起こす可能性があります。
DateTime
は、すべてのデータ(ulong
およびTicks
)とDateTimeKind
を含む単一のulong
フィールドのみを含む構造体です。 64ビット環境ではアトミックです。
それは、DateTime
もアトミックであることを意味しますか?または、次のコードはある時点で引き裂きにつながる可能性がありますか?
static DateTime _value;
static void Main()
{
for (int i = 0; i < 10; i++)
{
new Thread(_ =>
{
var random = new Random();
while (true)
{
_value = new DateTime((long)random.Next() << 30 | (long)random.Next());
}
}).Start();
}
Console.ReadLine();
}
ECMA仕様 セクション「I.12.6.6原子の読み取りと書き込み」から
準拠するCLIは、ネイティブWordサイズ(タイプ
native int
のサイズ)以下の適切に整列されたメモリ位置への読み取りおよび書き込みアクセスがアトミックであることを保証します(§I.12.6.2を参照)。場所は同じサイズです。アトミック書き込みは、書き込まれたもの以外のビットを変更しないものとします。明示的なレイアウト制御(パーティションII(インスタンスレイアウトの制御)を参照)を使用してデフォルトの動作を変更しない限り、自然なWordサイズ(native int
のサイズ)以下のデータ要素を適切に配置する必要があります。オブジェクト参照は、ネイティブのWordサイズで保存されているかのように扱われます。
native int
は、C#のIntPtr
です。
sizeof(IntPtr) >= sizeof(DateTime)
がランタイム環境(別名:64ビットとして実行)に当てはまる限り、[StructLayout(LayoutKind.Auto)]
itの代わりに、内部構造をバイトのずれた明示的なレイアウトに変更しません。現在、DateTime
構造体(またはこれらの規則に従うその他の構造体)の読み取りと書き込みは、ECMA仕様によってアトミックであることが保証されています。
64ビット環境で次のコードを実行することにより、それを確認できます。
public unsafe static void Main()
{
Console.WriteLine(sizeof(DateTime)); // Outputs 8
Console.WriteLine(sizeof(IntPtr)); // Outputs 8
Console.WriteLine(sizeof(ulong)); // Outputs 8
}
いくつかのテストを実行し、 上記の回答 に基づいて、今日アトミックであると言うのはかなり安全です。
Int64、DateTime、および128、192、256サイズの3つのカスタム構造体のN個のスレッドでX回の反復中に何個の涙が見つかるかを検証するためのテストを作成しました。
テストの構成:
私のマシンでの結果は次のとおりです(コアi7-4500U、Windows 10 x64、.NET 4.6、デバッグなしリリース、プラットフォームターゲット:コード最適化されたx64):
-------------- Trying to Tear --------------
Running: 64bits
Max Threads: 30
Max Reruns: 10
Iterations per Thread: 20000
--------------------------------------------
----- Tears ------ | -------- Size ---------
0 Int64 (64bits)
0 DateTime (64bits)
23 Struct128 (128bits)
87 Struct192 (192bits)
43 Struct256 (256bits)
----- Tears ------ | -------- Size ---------
0 Int64 (64bits)
0 DateTime (64bits)
44 Struct128 (128bits)
59 Struct192 (192bits)
52 Struct256 (256bits)
----- Tears ------ | -------- Size ---------
0 Int64 (64bits)
0 DateTime (64bits)
26 Struct128 (128bits)
53 Struct192 (192bits)
45 Struct256 (256bits)
----- Tears ------ | -------- Size ---------
0 Int64 (64bits)
0 DateTime (64bits)
46 Struct128 (128bits)
57 Struct192 (192bits)
56 Struct256 (256bits)
------------------- End --------------------
テストのコードはこちらにあります: https://Gist.github.com/Flash3001/da5bd3ca800f674082dd8030ef70cf4e
C#言語仕様から。
5.5変数参照の原子性次のデータ型の読み取りと書き込みは原子性です:bool、char、byte、sbyte、short、ushort、uint、int、float、および参照型。さらに、前のリストの基になる型を持つ列挙型の読み取りおよび書き込みもアトミックです。 long、ulong、double、decimal、およびユーザー定義型を含む他の型の読み取りおよび書き込みは、アトミックであることが保証されていません。その目的のために設計されたライブラリ関数は別として、インクリメントまたはデクリメントの場合など、アトミックな読み取り-変更-書き込みの保証はありません。