Bagaimana cara kerja atribut ThreadStatic?

140

Bagaimana cara [ThreadStatic]kerja atribut? Saya berasumsi bahwa kompilator akan memancarkan beberapa IL untuk mengisi / mengambil nilai di TLS, tetapi melihat pembongkaran tampaknya tidak melakukannya pada tingkat itu.

Sebagai tindak lanjut, apa yang terjadi jika Anda meletakkannya pada anggota non-statis? Kami meminta pengembang membuat kesalahan itu dan kompilator bahkan tidak memberikan peringatan.

Memperbarui

Pertanyaan kedua dijawab di sini: ThreadStatic Dimodifikasi dengan Static C #

joshperry
sumber
1
Jika IL yang dihasilkan sama (yang memang demikian), maka runtime harus dikodekan secara khusus untuk mengetahui bagaimana mengalokasikan & membaca nilai ketika mencapai bidang yang didekorasi seperti itu. Sepertinya hack :)
Rex M
Perhatikan bahwa jika ThreadStatic digunakan dengan thread Pool Thread, nilainya mungkin masih disetel saat berikutnya thread pool digunakan kembali (kecuali OS menjamin bahwa ia menginisialisasi ulang Thread Local Storage saat thread pool digunakan kembali) ). Untuk amannya, saat mulai menggunakan utas kumpulan, inisialisasi semua ThreadStatics ke keadaan yang diinginkan. Untuk membuatnya lebih mudah, kumpulkan ThreadStatics ke dalam kelas MyContext, jadi hanya satu statis untuk menginisialisasi ulang: public class SomeClass { public static MyContext myContext; ...Sekarang segera setelah mengambil untaian kumpulan, lakukanSomeClass.myContext = new MyContext();
ToolmakerSteve

Jawaban:

94

Semantik implementasi dari thread statis berada di bawah level IL, dalam compiler jit .NET. Kompiler yang memancarkan IL seperti VB.NET dan C # tidak perlu tahu apa-apa tentang Win32 TLS untuk mengeluarkan kode IL yang dapat membaca dan menulis variabel yang memiliki atribut ThreadStatic. Tidak ada yang istimewa tentang variabel sejauh yang diketahui C # - itu hanya lokasi untuk membaca dan menulis sesuatu. Fakta bahwa ia memiliki atribut di atasnya bukanlah konsekuensi dari C #. C # hanya perlu mengetahui untuk memancarkan IL membaca atau menulis instruksi untuk nama simbol itu.

'Pengangkatan berat' dilakukan oleh CLR inti yang bertanggung jawab untuk membuat IL bekerja pada arsitektur perangkat keras tertentu.

Itu juga akan menjelaskan mengapa menempatkan atribut pada simbol yang tidak sesuai (non-statis) tidak mendapatkan reaksi dari kompilator. Kompilator tidak mengetahui semantik khusus apa yang dibutuhkan atribut. Alat analisis kode seperti FX / Cop, bagaimanapun, harus mengetahuinya.

Cara lain untuk melihatnya: CIL mendefinisikan satu set cakupan penyimpanan: penyimpanan statis (global), penyimpanan anggota, dan penyimpanan tumpukan. TLS tidak ada dalam daftar itu, sangat mungkin karena TLS tidak perlu ada dalam daftar itu. Jika instruksi baca dan tulis IL cukup untuk mengakses TLS ketika simbol ditandai dengan atribut TLS, mengapa IL harus memiliki representasi atau perlakuan khusus untuk TLS? Itu tidak dibutuhkan.

dthorpe.dll
sumber
Tetapi bukankah perilaku khusus implementasi TLS itu benar-benar menumbangkan nilai jual yang "dapat diverifikasi" dari .NET / CLR?
Dai
Saya tidak tahu apa-apa tentang implikasi desain dari ini, tapi menurut saya cukup berguna. Saya telah menggunakannya di suatu tempat. Meskipun, saya ragu itu akan berfungsi pada aplikasi web menggunakan async / await, karena utas yang melanjutkan (setelah menunggu) mungkin utas yang berbeda. Saya pikir [ContextStatic]atribut menutupi kasus itu juga (dengan namanya menyiratkan itu berfungsi berdasarkan konteks utas), tetapi saya mungkin salah dalam asumsi saya. Saya melihat orang-orang online mengatakan [ContextStatic]atribut digunakan dengan Remoting.
user2173353
Apakah ada cara untuk mencakup semua kasus (multi-threading, aplikasi web multi-threading dengan async / await, dll.) Sehingga menjadi satu pekerjaan (misalnya permintaan web tunggal, pemrosesan pesan antrian tunggal, dll) dapatkan versi variabelnya sendiri, apa pun yang terjadi?
user2173353
118

Bagaimana cara kerja atribut [ThreadStatic]?

Anda dapat berpikir bahwa bidang yang ditandai dengan ThreadStatic dilampirkan ke utas dan masa pakainya sebanding dengan masa pakai utas.

Jadi dalam pseudocode ThreadStaticmirip (dengan semantik) memiliki nilai kunci yang dilampirkan ke utas:

Thread.Current["MyClass.myVariable"] = 1;
Thread.Current["MyClass.myvariable"] += 1;

tetapi sintaksnya sedikit lebih mudah:

class MyClass {
  [ThreadStatic]
  static int myVariable;
}
// .. then
MyClass.myVariable = 1;
MyClass.myVariable += 1;

apa yang terjadi jika Anda meletakkannya pada anggota non-statis?

Saya yakin ini diabaikan:

    class A {
        [ThreadStatic]
        public int a;
    }
    [Test]
    public void Try() {
        var a1 = new A();
        var a2 = new A();
        a1.a = 5;
        a2.a = 10;
        a1.a.Should().Be.EqualTo(5);
        a2.a.Should().Be.EqualTo(10);
    }

Selain itu, perlu disebutkan bahwa ThreadStatictidak memerlukan mekanisme sinkronisasi apa pun dibandingkan dengan bidang statis normal (karena status tidak dibagikan).

Dmytrii Nagirniak
sumber
1
Yang kedua seperti pseudo code seharusnya "MyClass.myVariable", bukan?
akshay2000
Saya tidak yakin batasan pastinya, tetapi saya hanya ingin menunjukkan jika tidak jelas bahwa itu tidak harus menjadi tipe primitif. Jika Anda melihat sumbernya, TransactionScopemereka menyimpan segala macam hal di sana untuk cakupan ( referensiource.microsoft.com/#System.Transactions/System/… )
Simon_Weaver
11

[ThreadStatic] membuat versi terisolasi dari variabel yang sama di setiap utas.

Contoh:

[ThreadStatic] public static int i; // Declaration of the variable i with ThreadStatic Attribute.

public static void Main()
{
    new Thread(() =>
    {
        for (int x = 0; x < 10; x++)
        {
            i++;
            Console.WriteLine("Thread A: {0}", i); // Uses one instance of the i variable.
        }
    }).Start();

    new Thread(() =>
   {
       for (int x = 0; x < 10; x++)
       {
           i++;
           Console.WriteLine("Thread B: {0}", i); // Uses another instance of the i variable.
       }
   }).Start();
}
Rui Ruivo
sumber
5

Bidang yang ditandai dengan [ThreadStatic]dibuat di Thread Local Storage sehingga setiap utas memiliki salinannya sendiri dari bidang tersebut, yaitu ruang lingkup bidang bersifat lokal ke utas.

Kolom TLS adalah akses melalui register segmen gs / fs. Segmen ini digunakan oleh kernel OS untuk mengakses memori khusus thread. Kompilator .net tidak memancarkan IL apa pun untuk memasukkan / mengambil nilai di TLS. Itu dilakukan oleh kernel OS.

Arif H-Shigri
sumber