ThreadStatic vs ThreadLocal <T>: ทั่วไปดีกว่าแอตทริบิวต์หรือไม่


97

[ThreadStatic]ถูกกำหนดโดยใช้แอตทริบิวต์ในขณะที่ThreadLocal<T>ใช้ทั่วไป เหตุใดจึงเลือกโซลูชันการออกแบบที่แตกต่างกัน ข้อดีและข้อเสียของการใช้ทั่วไปกับแอตทริบิวต์ในกรณีนี้คืออะไร?


4
ดูreedcopsey.com/2009/11/12/… - ฉันไม่เห็นว่ามันเกี่ยวอะไรกับการไตร่ตรองแม้ว่า ...
Jon Skeet

คำตอบ:


115

สิ่งที่โพสต์ในบล็อกระบุไว้ในความคิดเห็นไม่ได้ทำให้ชัดเจน แต่ฉันพบว่ามีความสำคัญมากคือ[ThreadStatic]ไม่ได้เริ่มต้นสิ่งต่างๆสำหรับทุกเธรดโดยอัตโนมัติ ตัวอย่างเช่นสมมติว่าคุณมีสิ่งนี้:

[ThreadStatic]
private static int Foo = 42;

หัวข้อแรกที่ใช้นี้จะเห็นเริ่มต้นได้ที่Foo 42แต่เธรดที่ตามมาจะไม่ โปรแกรมเริ่มต้นใช้งานได้กับเธรดแรกเท่านั้น ดังนั้นคุณต้องเขียนโค้ดเพื่อตรวจสอบว่ามีการเริ่มต้นหรือไม่

ThreadLocal<T> แก้ปัญหานั้นโดยให้คุณจัดหาฟังก์ชันการเริ่มต้น (ตามที่บล็อกของ Reed แสดง) ซึ่งทำงานก่อนครั้งแรกที่เข้าถึงรายการ

ในความคิดของฉันไม่เป็นประโยชน์อะไรกับการใช้แทน[ThreadStatic]ThreadLocal<T>


20
ยกเว้นบางทีที่ThreadLocal<T>มีให้บริการใน .NET 4 ขึ้นไปและแอตทริบิวต์ยังมีอยู่ใน 3.5 และด้านล่าง ThreadStatic
Jeroen

3
และถ้าคุณไม่ได้ใช้ initializers เพื่อตั้งค่า แต่จะตั้งค่าในภายหลังหลังจากเริ่มต้นใช้งานการใช้ [ThreadStatic] จะดีกว่า
ความคิด

9
และยกเว้นThreadLocal<T>การใช้งานนั้นIDisposableและมักจะบังคับให้คุณนำไปใช้IDisposableด้วยซึ่งบังคับให้ผู้โทรของคุณกำจัดคุณดังนั้นจึงนำไปใช้IDisposableเช่นกัน ...
Stefan Steinegger

4
@StefanSteinegger: ฉันจะระมัดระวังในการใช้ThreadLocalหรือThreadStaticกับเธรดพูล ค่าเหล่านี้จะยังคงอยู่ตลอดอายุของพูลเธรดไม่ใช่เฉพาะสำหรับงานที่คุณกำหนดเท่านั้น นั่นอาจทำให้คุณมีปัญหาในบางวิธีที่ไม่ชัดเจน ดูstackoverflow.com/questions/561518/…และคำถามที่คล้ายกันสำหรับข้อมูลเพิ่มเติม
Jim Mischel

3
ไม่ควรประกาศฟิลด์ในตัวอย่างด้วยstaticหรือไม่? ดูmsdn.microsoft.com/en-us/library/…
entheh

39

ThreadStatic Initialize บนเธรดแรกเท่านั้น ThreadLocal Initialize สำหรับแต่ละเธรด ด้านล่างนี้เป็นการสาธิตง่ายๆ:

    public static ThreadLocal<int> _threadlocal =
        new ThreadLocal<int>(() =>
        {
            return Thread.CurrentThread.ManagedThreadId;
        });

    public static void Main()
    {
        new Thread(() =>
        {
            for (int x = 0; x < _threadlocal.Value; x++)
            {
                Console.WriteLine("First Thread: {0}", x);
            }
        }).Start();

        new Thread(() =>
        {
            for (int x = 0; x < _threadlocal.Value; x++)
            {
                Console.WriteLine("Second Thread: {0}", x);
            }
        }).Start();

        Console.ReadKey();
    }

ป้อนคำอธิบายภาพที่นี่


16

ความคิดหลักที่อยู่เบื้องหลัง ThreadStatic คือการรักษาระดับแยกสำเนาของตัวแปรสำหรับแต่ละหัวข้อ

class Program
    {
        [ThreadStatic]
        static int value = 10;

        static void Main(string[] args)
        {
            value = 25;

            Task t1 = Task.Run(() =>
            {
                value++;
                Console.WriteLine("T1: " + value);
            });
            Task t2 = Task.Run(() =>
            {
                value++;
                Console.WriteLine("T2: " + value);
            });
            Task t3 = Task.Run(() =>
            {
                value++;
                Console.WriteLine("T3: " + value);
            });

            Console.WriteLine("Main Thread : " + value);

            Task.WaitAll(t1, t2, t3);
            Console.ReadKey();
        }
    }

ในตัวอย่างข้อมูลด้านบนเรามีสำเนาแยกต่างหากvalueสำหรับแต่ละเธรดรวมถึงเธรดหลักด้วย

ป้อนคำอธิบายภาพที่นี่

ดังนั้นตัวแปร ThreadStatic จะเริ่มต้นเป็นค่าเริ่มต้นในเธรดอื่นยกเว้นเธรดที่สร้างขึ้น

หากเราต้องการเริ่มต้นตัวแปรในแต่ละเธรดด้วยวิธีของเราเองให้ใช้ ThreadLocal


2
และบทความสมบูรณ์สามารถพบได้ที่นี่
Daniel Dušek
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.