环境:
- 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();
}
}
}