環境:
- window 10
- .netcore 3.1.1
- vs 16.4.5
說明:
要達到的效果是,每一個線程都有一個單獨的容器用來存儲數據。實現的方法有三種:線程內的數據槽、ThreadStatic特性、ThreadLocal<T>對象
一、線程內的數據槽
線程中的數據槽(LocalDataStoreSlot
)是一種給線程存儲數據的容器,一個槽只能存一個數據,一個線程可以有很多個槽。可以給槽命名,已命名的槽和線程建立了關聯,你可以在任何地方從線程中獲取命名的數據槽(GetNamedDataSlot("name-slot")
)。注意:數據存儲在槽內的,所以線程存取數據都是要指定具體槽的SetData(slot, "xiaoming")
,GetData(slot)
。還有,槽是對所有線程都有效的,只不過不同的線程的同一個槽內存取的是不同的數據。
線程中的數據槽有兩種,未命名的和已命名的:
- 未命名的數據槽:
只能通過AllocateDataSlot()方法獲取,獲取後如果想使用,那麼所有線程都必須能引用到獲取的槽。 - 命名數據槽:
可以通過GetNamedDataSlot(“slotname”)或AllocateNamedDataSlot(“slot-name”)獲取,獲取後這個槽本身就和線程建立了關聯(以槽名稱作爲紐帶),這樣當線程需要時直接通過GetNamedDataSlot獲取就行了,當需要清除關聯時使用Thread.FreeNamedDataSlot(“names-slot”)方法清除這個關聯,注意這個只是清除關聯,不會清除已獲取的槽中的數據,所以即使清除關聯後,你只要有這個slot的引用,也是可以進行正常的存取數據的。如果不清楚關聯的話AllocateNamedDataSlot方法發現已經有命名的槽的話就會報異常,而GetNamedDataSlot方法不會。
下面看看數據槽的使用示例:
namespace ConsoleApp1
{
class Program
{
static void Main(string[] args)
{
//Test1();
//Test2();
//Test3();
Console.WriteLine("ok");
Console.ReadLine();
}
/// <summary>
/// 測試命名線程槽的GetNamedDataSlot方法
/// </summary>
private static void Test3()
{
LocalDataStoreSlot slot = Thread.GetNamedDataSlot("names-slot");
var thread = new Thread(() =>
{
var slot2 = Thread.GetNamedDataSlot("names-slot");
Console.WriteLine("slot==slot2=>" + (slot == slot2).ToString());
});
thread.Start();
Thread.FreeNamedDataSlot("names-slot");
var slot3 = Thread.AllocateNamedDataSlot("names-slot");
var slot4 = Thread.AllocateNamedDataSlot("names-slot");
Console.WriteLine("slot3==slot4 -> " + (slot3 == slot4));
}
/// <summary>
/// 測試命名的線程槽
/// </summary>
private static void Test2()
{
LocalDataStoreSlot slot = Thread.AllocateNamedDataSlot("names-slot");
var thread = new Thread(() =>
{
Thread.SetData(slot, "xiaoming");
var data = Thread.GetData(slot);
Console.WriteLine("work-thread-slot:" + data);
Thread.FreeNamedDataSlot("names-slot");
data = Thread.GetData(slot);
var slot2 = Thread.GetNamedDataSlot("names-slot");
data = Thread.GetData(slot2);
Console.WriteLine("slot==slot2" + (slot == slot2).ToString());
Console.WriteLine("work-thread-slot:" + data);
Thread.SetData(slot, "xiaohong");
Console.WriteLine("work-thread-slot:" + Thread.GetData(slot));
});
thread.Start();
Thread.Sleep(3000);
var data2 = Thread.GetData(slot);
Console.WriteLine("main-thread-slot:" + data2);
}
/// <summary>
/// 測試未命名的線程槽
/// </summary>
private static void Test1()
{
LocalDataStoreSlot slot = Thread.AllocateDataSlot();
Thread.SetData(slot, "xiaoming");
var data = Thread.GetData(slot);
var thread = new Thread(() =>
{
var data2 = Thread.GetData(slot);
Console.WriteLine("work-thread-slot:" + data2);
});
thread.Start();
Console.WriteLine("main-thread-slot:" + data);
}
}
}
二、ThreadStatic
這種方法是在靜態字段上添加[ThreadStatic]特性,然後每個線程訪問時都是不同的值。
注意:這要求字段必須是靜態的,如果不是靜態的那麼加上[ThreadStatic]特性也沒用,因爲字段不是特性的話,它就屬於一個實例對象,沒辦法在一個實例對象內根據線程進行隔離。
如果這個字段是靜態的,那麼這個字段是屬於類的,它對所有的線程都一樣,此時再給它加上[ThreadStatic]特性就會把這個靜態字段進行線程隔離。
下面來看實驗代碼:
[ThreadStatic]
private static string name = string.Empty;
static void Main(string[] args)
{
var thread = new Thread(() =>
{
name = "xiaoming";
Console.WriteLine("work name=" + name);
});
thread.Start();
thread.Join();
Console.WriteLine("main name=" + name);
Console.WriteLine("ok");
Console.ReadLine();
}
三、ThreadLocal
這是一個容器,提供了一個對象專門用來存儲和線程相關的對象,它和[ThreadStatic]最大的不同是:不再要求字段是靜態的。
具體使用方式如下:
class Program
{
private ThreadLocal<string> username = new ThreadLocal<string>();
static void Main(string[] args)
{
var pro = new Program();
var thread = new Thread(() =>
{
pro.username.Value = "xiaoming";
Console.WriteLine("worker username=" + pro.username.Value);
});
thread.Start();
thread.Join();
Console.WriteLine("worker username=" + pro.username.Value);
Console.WriteLine("ok");
Console.ReadLine();
}
}
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace ConsoleApp1
{
class Program
{
#region 測試ThreadStatic、ThreadLocal本地存儲
/*
* 測試ThreadStatic、ThreadLocal本地存儲
*/
//[ThreadStatic]
//private static string name = string.Empty;
//static void Main(string[] args)
//{
// ThreadLocal<string> userName = new ThreadLocal<string>();
// var thread = new Thread(() =>
// {
// userName.Value = "xiaohua";
// name = "xiaoming";
// Console.WriteLine("work name=" + name);
// Console.WriteLine("work userName=" + userName.Value);
// });
// thread.Start();
// thread.Join();
// Console.WriteLine("main name=" + name);
// Console.WriteLine("main userName=" + userName.Value);
// Console.WriteLine("ok");
// Console.ReadLine();
//}
#endregion
#region Debug和Releas模式的運行速度對比
//public static void Main(string[] args)
//{
// //for (int i = 0; i < 10000; i++)
// //{
// // int res = new Random().Next(1, 100000);
// // File.AppendAllText("c:\\temp\\ints.txt", res + "\r\n");
// //}
// //Release和Debug模式的區別
// var datas = File.ReadAllLines("c:\\temp\\ints.txt").Select(i => int.Parse(i)).ToList<int>();
// for (int i = 0; i < 5; i++)
// {
// var arr = new int[datas.Count];
// datas.CopyTo(arr);
// Stopwatch watch = new Stopwatch();
// watch.Start();
// Order(arr);
// watch.Stop();
// Console.WriteLine(watch.Elapsed);
// }
// Console.WriteLine("ok");
// Console.Read();
//}
//public static int[] Order(int[] datas)
//{
// for (int i = 0; i < datas.Length; i++)
// {
// for (int j = i + 1; j < datas.Length; j++)
// {
// if (datas[i] < datas[j])
// {
// int t = datas[i];
// datas[i] = datas[j];
// datas[j] = t;
// }
// }
// }
// return datas;
//}
#endregion
public static void Main(string[] args)
{
var isStop = 0;
var t = new Thread(() =>
{
var isSuccess = true;
while (isStop == 0)
{
//Thread.VolatileRead(ref isStop);
isSuccess = !isSuccess;
}
});
t.Start();
Thread.Sleep(1000);
isStop = 1;
t.Join();
Console.WriteLine("ok");
Console.ReadLine();
//bool isStop = false;
//var thread = new Thread(() =>
// {
// var isSuccess = false;
// while (!isStop)
// {
// //這個方法可以指示從內存中讀寫
// //Thread.MemoryBarrier();
// isSuccess = !isSuccess;
// }
// });
//thread.Start();
//Thread.Sleep(1000);
//isStop = true;
//thread.Join();
//Console.WriteLine("ok");
//Console.ReadLine();
//下面不會出現bug
//bool isStop = false;
//var thread = new Thread(() =>
//{
// while (true)
// {
// if (isStop) break;
// }
//});
//thread.Start();
//Thread.Sleep(1000);
//isStop = true;
//thread.Join();
//Console.WriteLine("ok");
//Console.ReadLine();
}
}
}