如何保证对象在线程内唯一
背景:为了解决多线程竞用共享资源的问题,引入数据槽的概念,即将数据存放到线程的环境块中,使该数据只能单一线程访问.(属于线程空间上的开销)
下面的三种方式是解决多线程竞用共享资源的通用方式:
①:AllocateNamedDataSlot命名槽位和AllocateDataSlot未命名槽位 解决线程竞用资源共享问题。
(PS:在主线程上设置槽位,使该数据只能被主线程读取,其它线程无法访问)
private void button10_Click(object sender, EventArgs e)
{
#region 01-AllocateNamedDataSlot命名槽位
{
var d = Thread.AllocateNamedDataSlot("userName");
//在主线程上设置槽位,使该数据只能被主线程读取,其它线程无法访问
Thread.SetData(d, "ypf");
//声明一个子线程
var t1 = new Thread(() =>
{
Console.WriteLine("子线程中读取数据:{0}", Thread.GetData(d));
});
t1.Start();
//主线程中读取数据
Console.WriteLine("主线程中读取数据:{0}", Thread.GetData(d));
}
#endregion
#region 02-AllocateDataSlot未命名槽位
{
var d = Thread.AllocateDataSlot();
//在主线程上设置槽位,使该数据只能被主线程读取,其它线程无法访问
Thread.SetData(d, "ypf");
//声明一个子线程
var t1 = new Thread(() =>
{
Console.WriteLine("子线程中读取数据:{0}", Thread.GetData(d));
});
t1.Start();
//主线程中读取数据
Console.WriteLine("主线程中读取数据:{0}", Thread.GetData(d));
}
#endregion
}

②:利用特性[ThreadStatic] 解决线程竞用资源共享问题
(PS:在主线程中给ThreadStatic特性标注的变量赋值,则只有主线程能访问该变量,比数据槽性能好,使用方便)



③:利用ThreadLocal线程的本地存储, 解决线程竞用资源共享问题(线程可见性)
(PS: 在主线程中声明ThreadLocal变量,并对其赋值,则只有主线程能访问该变量)


④ CallContext 线程内可见。当 CallContext 沿执行代码路径往返传播并且由该路径中的各个对象检查时,可将对象添加到其中。
使用场景:当对象需要线程内全局使用。Core不支持
CallContext.SetData(key, temp); DbContext temp = CallContext.GetData(key) as DbContext;
四. 内存栅栏-线程共享资源
背景:当多个线程共享一个变量的时候,在Release模式的优化下,子线程会将共享变量加载的cup Cache中,导致主线程不能使用该变量而无法运行。
内存栅栏:变量在进入内存栅栏并读取时,变量的值已经写入完毕,离开栅栏时,变量的值被读取时也已经写入完毕
解决方案:
①:不要让多线程去操作同一个共享变量,从根本上解决这个问题。
②:利用MemoryBarrier方法进行处理,在此方法之前的内存写入都要及时从cpu cache中更新到 memory;在此方法之后的内存读取都要从memory中读取,而不是cpu cache。
③:利用VolatileRead/Write方法进行处理。
a = 0 , b = 0; void fun1() { a = 1; b = 1; } void fun2() { while (b == 0) continue; assert(a == 1); }
如果 一个线程执行 fun1(),另一个线程执行 fun2时,当b=1,a一定等于 1吗,答案是 不一定,a还有可能是0 具体请看 https://zhuanlan.zhihu.com/p/125737864
1 private void button11_Click(object sender, EventArgs e)
2 {
3 #region 01-默认情况(Release模式主线程不能正常运行)
4 //{
5 // var isStop = false;
6 // var t = new Thread(() =>
7 // {
8 // var isSuccess = false;
9 // while (!isStop)
10 // {
11 // isSuccess = !isSuccess;
12 // }
13 // Console.WriteLine("子线程执行成功");
14 // });
15 // t.Start();
16
17 // Thread.Sleep(1000);
18 // isStop = true;
19
20 // t.Join();
21 // Console.WriteLine("主线程执行结束");
22 //}
23 #endregion
24
25 #region 02-MemoryBarrier解决共享变量(Release模式下可以正常运行)
26 //{
27 // var isStop = false;
28 // var t = new Thread(() =>
29 // {
30 // var isSuccess = false;
31 // while (!isStop)
32 // {
33 // Thread.MemoryBarrier();
34
35 // isSuccess = !isSuccess;
36 // }
37 // Console.WriteLine("子线程执行成功");
38 // });
39 // t.Start();
40
41 // Thread.Sleep(1000);
42 // isStop = true;
43
44 // t.Join();
45 // Console.WriteLine("主线程执行结束");
46 //}
47 #endregion
48
49 #region 03-VolatileRead解决共享变量(Release模式下可以正常运行)
50 {
51 var isStop = 0;
52 var t = new Thread(() =>
53 {
54 var isSuccess = false;
55 while (isStop == 0)
56 {
57 Thread.VolatileRead(ref isStop);
58
59 isSuccess = !isSuccess;
60 }
61 Console.WriteLine("子线程执行成功");
62 });
63 t.Start();
64
65 Thread.Sleep(1000);
66 isStop = 1;
67
68 t.Join();
69 Console.WriteLine("主线程执行结束");
70 }
71 #endregion
72
73
74 }

浙公网安备 33010602011771号