线程安全和中间量 Lock

         {
                    List<int> intlist = new List<int>();
                    for (int i = 0; i < 10000; i++)
                    {
                        intlist.Add(i);
                    }
                }//正常for循环完毕是   i=9999
                {
                    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) 标准写法:    

private readonly static object obj_Lock = new object();            
lock (obj_Lock) { intlist.Add(i); }

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

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

 //分块、分区执行解决线程安全问题
                {
    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 换成安全对象;

  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();

中间变量

 {
    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了

可以另外定义个变量---变量,在每次循环的时候,赋值;循环多少次,就会有多少个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());
    }
注意这个变量一定要在循环内部去定义,在外部定义在循环内部赋值是不解决问题的

  

posted @ 2021-12-03 11:53  wolfsocket  阅读(33)  评论(0)    收藏  举报