1. 程式人生 > >轉-NET(C#):線程本地存儲(Thread-Local Storage)之ThreadStatic, LocalDataStoreSlot和ThreadLocal<T>

轉-NET(C#):線程本地存儲(Thread-Local Storage)之ThreadStatic, LocalDataStoreSlot和ThreadLocal<T>

輸出 修改 pla star 目錄 div .net 4.0 current cor

NET(C#):線程本地存儲(Thread-Local Storage)之ThreadStatic, LocalDataStoreSlot和ThreadLocal<T>

目錄

  • 1. 使用ThreadStatic特性
  • 2. 使用命名的LocalDataStoreSlot類型
  • 3. 使用未命名的LocalDataStoreSlot類型
  • 4. 使用.NET 4.0的ThreadLocal<T>類型
  • 5. 強調一下不同方法和TLS的默認值

註:為了簡潔,下面將以”TLS”三個字母代替”線程本地存儲(Thread-Local Storage)”。

1. 使用ThreadStatic特性

ThreadStatic特性是最簡單的TLS使用,且只支持靜態字段,只需要在字段上標記這個特性就可以了:

//TLS中的str變量

[ThreadStatic]

static string str = "hehe";

static void Main()

{

//另一個線程只會修改自己TLS中的str變量

Thread th = new Thread(() => { str = "Mgen"; Display(); });

th.Start();

th.Join();

Display();

}

static void Display()

{

Console.WriteLine("{0} {1}", Thread.CurrentThread.ManagedThreadId, str);

}

運行結果:

3 Mgen

1 hehe

可以看到,str靜態字段在兩個線程中都是獨立存儲的,互相不會被修改。

返回目錄

2. 使用命名的LocalDataStoreSlot類型

顯然ThreadStatic特性只支持靜態字段太受限制了。.NET線程類型中的LocalDataStoreSlot提供更好的TLS支持。我們先來看看命名的LocalDataStoreSlot類型,可以通過Thread.AllocateNamedDataSlot來分配一個命名的空間,通過Thread.FreeNamedDataSlot來銷毀一個命名的空間。空間數據的獲取和設置則通過Thread類型的GetData方法和SetData方法。

來看代碼:

static void Main()

{

//創建Slot

LocalDataStoreSlot slot = Thread.AllocateNamedDataSlot("slot");

//設置TLS中的值

Thread.SetData(slot, "hehe");

//修改TLS的線程

Thread th = new Thread(() =>

{

Thread.SetData(slot, "Mgen");

Display();

});

th.Start();

th.Join();

Display();

//清除Slot

Thread.FreeNamedDataSlot("slot");

}

//顯示TLS中Slot值

static void Display()

{

LocalDataStoreSlot dataslot = Thread.GetNamedDataSlot("slot");

Console.WriteLine("{0} {1}", Thread.CurrentThread.ManagedThreadId, Thread.GetData(dataslot));

}

輸出:

3 Mgen

1 hehe

返回目錄

3. 使用未命名的LocalDataStoreSlot類型

線程同樣支持未命名的LocalDataStoreSlot,未命名的LocalDataStoreSlot不需要手動清除,分配則需要Thread.AllocateDataSlot方法。註意由於未命名的LocalDataStoreSlot沒有名稱,因此無法使用Thread.GetNamedDataSlot方法,只能在多個線程中引用同一個LocalDataStoreSlot才可以對TLS空間進行操作,將上面的命名的LocalDataStoreSlot代碼改成未命名的LocalDataStoreSlot執行:

//靜態LocalDataStoreSlot變量

static LocalDataStoreSlot slot;

static void Main()

{

//創建Slot

slot = Thread.AllocateDataSlot();

//設置TLS中的值

Thread.SetData(slot, "hehe");

//修改TLS的線程

Thread th = new Thread(() =>

{

Thread.SetData(slot, "Mgen");

Display();

});

th.Start();

th.Join();

Display();

}

//顯示TLS中Slot值

static void Display()

{

Console.WriteLine("{0} {1}", Thread.CurrentThread.ManagedThreadId, Thread.GetData(slot));

}

輸出和上面的類似。

返回目錄

4. 使用.NET 4.0的ThreadLocal<T>類型

.NET 4.0在線程方面加入了很多東西,其中就包括ThreadLocal<T>類型,他的出現更大的簡化了TLS的操作。ThreadLocal<T>類型和Lazy<T>驚人相似,構造函數參數是Func<T>用來創建對象(當然也可以理解成對象的默認值),然後用Value屬性來得到或者設置這個對象。

ThreadLocal的操作或多或少有點像上面的未命名的LocalDataStoreSlot,但ThreadLocal感覺更簡潔更好理解。

代碼:

static ThreadLocal<string> local;

static void Main()

{

//創建ThreadLocal並提供默認值

local = new ThreadLocal<string>(() => "hehe");

//修改TLS的線程

Thread th = new Thread(() =>

{

local.Value = "Mgen";

Display();

});

th.Start();

th.Join();

Display();

}

//顯示TLS中數據值

static void Display()

{

Console.WriteLine("{0} {1}", Thread.CurrentThread.ManagedThreadId, local.Value);

}

輸出:

3 Mgen

1 hehe

返回目錄

5. 強調一下不同方法和TLS的默認值

上面代碼都是一個一個線程設置值,另一個線程直接修改值然後輸出,不會覺察到TLS中默認值的狀況,下面專門強調一下不同方法的默認值狀況。

ThreadStatic不提供默認值:

[ThreadStatic]

static int i = 123;

static void Main()

{

//輸出本地線程TLS數據值

Console.WriteLine(i);

//輸出另一個線程TLS數據值

ThreadPool.QueueUserWorkItem(_ => Console.WriteLine(i));

//控制臺等待線程結束

Console.ReadKey();

}

輸出:

123

0

顯然本地線程TLS數據時123,而靜態變量的默認值不會在另一個線程中初始化的。

LocalDataStoreSlot很容易可以看出來,不可能有默認值,因為初始化只能構造一個空間,而不能賦予它值,Thread.SetData顯然只會在TLS中設置數據,還是用代碼演示一下:

static LocalDataStoreSlot slot = Thread.AllocateDataSlot();

static void Main()

{

Thread.SetData(slot, 123);

//輸出本地線程TLS數據值

Console.WriteLine(Thread.GetData(slot));

//輸出另一個線程TLS數據值

ThreadPool.QueueUserWorkItem(_ => Console.WriteLine(Thread.GetData(slot) == null));

//控制臺等待線程結束

Console.ReadKey();

}

輸出:

123

True

第二行是True,那麽另一個線程中的數據是null。

最後重點:.NET 4.0後的ThreadLocal會提供默認值的,還記得我上面說的那句話“ThreadLocal的操作或多或少有點像上面的未命名的LocalDataStoreSlot”?有人可能會問那為什麽要創造出ThreadLocal?還有一個很大的區別ThreadLocal可以提供TLS中數據的默認值。(另外還有ThreadLocal是泛型類,而LocalDataStoreSlot不是)。

代碼:

static ThreadLocal<int> local = new ThreadLocal<int>(() => 123);

static void Main()

{

//輸出本地線程TLS數據值

Console.WriteLine(local.Value);

//輸出另一個線程TLS數據值

ThreadPool.QueueUserWorkItem(_ => Console.WriteLine(local.Value));

//控制臺等待線程結束

Console.ReadKey();

}

輸出:

123

123

轉-NET(C#):線程本地存儲(Thread-Local Storage)之ThreadStatic, LocalDataStoreSlot和ThreadLocal<T>