线程安全和中间量 Lock

1
2
3
4
5
6
7
{
           List<int> intlist = new List<int>();
           for (int i = 0; i < 10000; i++)
           {
               intlist.Add(i);
           }
       }//正常for循环完毕是   i=9999
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
{
    List<int> intlist = new List<int>();
    List<Task> tasklist = new List<Task>();
    int k = 0;
    for (int i = 0; i < 10000; i++)
    {
        Task.Run(() =>
        {
            lock (obj_Lock)
            {
                intlist.Add(i);
            }
        });
    }
    Task.WaitAll(tasklist.ToArray());   
//多线程循环完 k肯定是小于10000的

  线程安全:一段业务逻辑,单线程执行和多线程执行后的结果如果完全一致,是线程安全的;否则就表示是线程不安全;

1.锁,加索--直接是标准锁-- - 锁的本质,是独占引用;加锁以后;反多线程-- - 不再是多线程执行了 - 可以解决线程安全问题-- - 不推荐大家使用--加锁--影响性能

    a) 标准写法:    

1
2
3
4
<strong>private readonly static object obj_Lock = new object();</strong>            <br> <strong>lock (obj_Lock)
                            {
                                intlist.Add(i);
                            }                   </strong>

    b) 锁对象--不要去锁String 锁 This

2.直接使用单线程?(分块/分区去执行,每一个区块对应一个线程) 把要操作的整块数据,做一个切分:例如有1000条数据,可以把这一万条数据分区、分块;分三块;每一块开启一个线程去执行;三个线程
每一个线程内部执行的动作是单线程--线程安全等待三个线程执行结束以后,再单线程做一个统一汇总;需要把程序加以设计才能完成;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
//分块、分区执行解决线程安全问题
                {
    List<int> intlist1 = new List<int>();
    List<int> intlist2 = new List<int>();
    List<int> intlist3 = new List<int>();
 
    int length = 10000;
    int Num1 = 3000;
    int Num2 = 6000;
    int Num3 = 10000;
 
    List<Task> taskList = new List<Task>();
    taskList.Add(Task.Run(() =>
    {
        for (int i = 0; i < Num1; i++)
        {
            intlist1.Add(i);
        }
    }));
    taskList.Add(Task.Run(() =>
    {
        for (int i = Num1; i < Num2; i++)
        {
            intlist2.Add(i);
        }
    }));
 
    taskList.Add(Task.Run(() =>
    {
        for (int i = Num2; i < Num3; i++)
        {
            intlist3.Add(i);
        }
    }));
    Task.WaitAll(taskList.ToArray());
 
    intlist1.AddRange(intlist2);
    intlist1.AddRange(intlist3);
}
            }

  3.使用线程安全对象  看看数据结构 线程安全对象  List/Arraylist 都不是线程安全的集合--把list  Arraylist 换成安全对象;

1
2
3
4
5
6
7
BlockingCollection<int> blockinglist = new BlockingCollection<int>();
      ConcurrentBag<int> conocurrentbag = new ConcurrentBag<int>();
      ConcurrentDictionary<string, int> concurrentDictionary = new ConcurrentDictionary<string, int>();
      ConcurrentQueue<int> concurrentQueue = new ConcurrentQueue<int>();
      ConcurrentStack<int> concurrentStack = new ConcurrentStack<int>();
      OrderablePartitioner<int> OrderablePartitioner = new OrderablePartitioner<int>();
      Partitioner<int> partitionerArray = new Partitioner();

中间变量

1
2
3
4
5
6
7
{
    for (int i = 0; i< 5; i++)
    {
 
            Debug.WriteLine($"ThreadID={Thread.CurrentThread.ManagedThreadId.ToString("00")}_i={i}");
    }
}   //每次打印的i都为5    在循环内部:如果直接变量i,当线程执行的时候:i会变成5;

Task开启线程的时候,延迟开启--(延迟时间很短的); 开启多线程执行不会阻塞主线程;在循环的时候,不会阻塞主线程,循环很快;当有一个线程在正式的执行业务逻辑的时候;循环已经结束了;i已经变成5了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
可以另外定义个变量---变量,在每次循环的时候,赋值;循环多少次,就会有多少个k,每个线程使用的是每一次循环内部的k;
在多线程中,使用这个变量的值
{
 
        List<Task> tasks = new List<Task>();
        for (int i = 0; i < 5; i++)
        {
            int k = 0;
            tasks.Add(Task.Run(() =>
            {
                Debug.WriteLine($"ThreadID={Thread.CurrentThread.ManagedThreadId.ToString("00")}_i={i}__ k={ k}");
            }));
        }
 
        Task.WaitAll(tasks.ToArray());
    }<br>注意这个变量一定要在循环内部去定义,在外部定义在循环内部赋值是不解决问题的

  

posted @   wolfsocket  阅读(29)  评论(0编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示