其他分享
首页 > 其他分享> > 测试一次多线程

测试一次多线程

作者:互联网

  1 using System;
  2 using System.Collections.Generic;
  3 using System.Diagnostics;
  4 using System.Linq;
  5 using System.Text;
  6 using System.Threading;
  7 using System.Threading.Tasks;
  8 
  9 namespace AsyncTask
 10 {
 11     class Program
 12     {
 13         /*
 14          前台线程和后台线程的区别
 15                  应用程序的主线程以及使用Thread构造的线程都默认为前台线程
 16         使用Thread建立的线程默认情况下是前台线程,在进程中,只要有一个前台线程未退出,进程就不会终止.
 17         主线程就是一个前台线程。而后台线程不管线程是否结束,只要所有的前台线程都退出(包括正常退出和异常退出)后,进程就会自动终止.
 18         一般后台线程用于处理时间较短的任务,如在一个Web服务器中可以利用后台线程来处理客户端发过来的请求信息。而前台线程一般用于处理需要长时间等待的任务,如在Web服务器中的监听客户端请求的程序,或是定时对某些系统资源进行扫描的程序
 19          */
 20 
 21         /*
 22          * 异步和多线程有什么区别
 23          *          https://www.cnblogs.com/gbat/articles/6385182.html
 24          *          这个网址说的还可以
 25          * 
 26          * 
 27          * 
 28          异步多线程:什么时候用多线程?  提升速度==>可以并发运行为前提,不要强行拆分
 29                 例子:一个数据库,多条语句执行,即使拆分开了,还是要一条一条执行;;  监控执行监控的,高空抛物执行高空抛物的
 30                 
 31             场景1:项目经理 制定好 项目计划,  分发任务,   但是项目经理还要和甲方说 可以验收了,
 32                       如果项目经理属于一个线程,  其他人属于一个线程, 那么  
 33                       告知甲方可以验收就会在编码前边
 34                       这怎么搞?
 35 
 36 
 37             whenall  whenany和waitall  waitany
 38             when这一类得如果说是不需要给用户反馈的话,就可以用(比如写个日志,发个邮件啥得)
 39             wait这一类得话,可以是给用户一个反馈得,有些时候必须要阻塞一下
 40 
 41             自己想得:when这一类得话,可以用ContinueWith做回调,  也可以给用户反馈
 42              */
 43 
 44 
 45         //加锁后
 46         //private 防止外边的也去lock   static 全场唯一     readonly  不能改动   object 表示引用
 47         private static readonly object myLock = new Object();//微软官方推荐
 48         static void Main(string[] args)
 49         {
 50             #region  第一部分   Task.Run();
 51             //Console.WriteLine($"项目经理启动了一个项目:==={Thread.CurrentThread.ManagedThreadId.ToString()}===");
 52             //Console.WriteLine($"项目经理分发任务:==={Thread.CurrentThread.ManagedThreadId.ToString()}===");
 53 
 54 
 55             ////创建线程容器,把编码线程放进去,加入阻塞
 56             //List<Task> tasks = new List<Task>();
 57             ////开启新线程
 58             //tasks.Add(Task.Run(() => Console.WriteLine($"夏侯惇*****收到任务开始编程:++++{Thread.CurrentThread.ManagedThreadId.ToString()}++++")));
 59             //tasks.Add(Task.Run(() => Console.WriteLine($"曹仁*****收到任务开始编程:++++{Thread.CurrentThread.ManagedThreadId.ToString()}++++")));
 60             //tasks.Add(Task.Run(() => Console.WriteLine($"张辽*****收到任务开始编程:++++{Thread.CurrentThread.ManagedThreadId.ToString()}++++")));
 61             //tasks.Add(Task.Run(() => Console.WriteLine($"徐晃*****收到任务开始编程:++++{Thread.CurrentThread.ManagedThreadId.ToString()}++++")));
 62 
 63             ////Task.WaitAny(tasks.ToArray());//这里会阻塞线程,等待  =某= 个任务完成后,就会进入下一行  卡界面
 64             ////Console.WriteLine($"这里有奖品,先到先得");
 65             ////Task.WaitAll(tasks.ToArray(),1000);//这里会等待子线程执行一秒钟  ,一秒以后会继续执行,如果一秒内执行完了,OK继续,这里意思是最多等待1秒
 66             ////Console.WriteLine("1秒以后,我会继续执行");
 67 
 68             ///*多线程加快速度  ,但是全部任务完成以后才可以执行下面这个验收    那么主线程(经理线程就要 阻塞一下)*/
 69             //Task.WaitAll(tasks.ToArray());//这里会阻塞当前线程,等着全部任务完成后, 再进行下一行  会卡界面
 70             //Console.WriteLine($"项目经理告知甲方开始验收项目了!!!!!:==={Thread.CurrentThread.ManagedThreadId.ToString()}===");
 71 
 72 
 73             /*如果想要不卡界面怎么整呢
 74                ContinueWith  做一个延续,其实就是个回调函数,  当前面得任务完成后, 直接就做接下来得动作
 75              */
 76             //Task.WhenAny(tasks.ToArray()).ContinueWith(m => Console.WriteLine("用我可以不用等待,不回卡界面,我是某个完成任务,就可以"));
 77             //Task.WhenAll(tasks.ToArray()).ContinueWith(m=>Console.WriteLine("用我可以不用等待,不回卡界面,我是全部执行完的任务"));
 78             #endregion
 79 
 80             #region  第二部分  模拟执行10000个任务,但是只有10个线程
 81             /*
 82              模拟执行10000个任务,但是只有10个线程
 83              
 84              */
 85             //模拟任务数量
 86             //List<int> list = new List<int>();
 87             //for (int i = 0; i < list.Count; i++)
 88             //{
 89             //    list.Add(i);
 90             //}
 91             ////模拟任务
 92             //Action<int> action = i =>
 93             //{
 94             //    Console.WriteLine(Thread.CurrentThread.ManagedThreadId.ToString());
 95             //    Thread.Sleep(new Random().Next(100,300));
 96             //};
 97 
 98             //List<Task> tasks = new List<Task>();
 99 
100             //foreach (var item in list)
101             //{
102             //    int k = item;
103             //    tasks.Add(Task.Run(()=>action.Invoke(k)));//开启线程任务,并封装近Task集合中
104             //    //判断线程执行数量
105             //    if (tasks.Count>10)//>10说明至少10个了,
106             //    {
107             //        /*
108             //         一旦超过10个线程任务,那就得在这等着,等着玩意有任务完成了,那么就将已经完成得清理掉,进行下一次循环
109             //         */
110             //        Task.WaitAny(tasks.ToArray());//有一个执行完就,就开始下一步
111             //        tasks = tasks.Where(t=>t.Status!=TaskStatus.RanToCompletion).ToList();//将已经完成的进行过滤,
112             //    }
113             //}
114             //Task.WaitAll(tasks.ToArray());
115 
116 
117 
118             #endregion
119 
120             #region  第三部分   Task.Delay  和  Thread.Sleep()  的区别
121             /*
122              * Task.Delay  和  Thread.Sleep()  的区别?
123              Task.Delay不会卡界面,不会让用户等待,可以继续操作, 过一段时间后,才开始反馈信息
124              Thread.Sleep() 会卡界面, 会让用户等待, 不可以继续操作,  只有等睡足了,可开始反馈
125              */
126 
127             Stopwatch stopwatch = new Stopwatch();//计时器
128                                                   //方式1
129                                                   //stopwatch.Start();//开始计时
130                                                   //Thread.Sleep(2000);//睡觉2秒
131                                                   //stopwatch.Stop();
132                                                   //Console.WriteLine(stopwatch.ElapsedMilliseconds);
133 
134             //方式2
135             //Stopwatch stopwatch = new Stopwatch();//计时器
136             //stopwatch.Start();//开始计时
137             //Task.Delay(2000).ContinueWith(t =>
138             //{
139             //    stopwatch.Stop();
140             //    Console.WriteLine(stopwatch.ElapsedMilliseconds);
141             //});
142 
143             //方式3   
144             //stopwatch.Start();//开始计时
145             //Task.Run(()=> {
146             //    Thread.Sleep(2000);//睡觉2秒
147             //    stopwatch.Stop();
148             //    Console.WriteLine(stopwatch.ElapsedMilliseconds);
149             //});
150             #endregion
151 
152             #region   第四部分  parallel  控制并发量,  比上边的第二部分更简洁
153 
154             /*   
155                 要求:最多3个线程,  并发执行10个任务
156              
157              */
158             //ParallelOptions parallelOptions = new ParallelOptions();//这个可以控制并发数量(即线程数量)
159             //parallelOptions.MaxDegreeOfParallelism = 3;//最多3个线程的要求    在这里可以实现
160             //Parallel.For(0,10,parallelOptions,i=>Console.WriteLine("高薪就业"));
161 
162             /*
163              上边的要卡线程,卡屏,那么要不卡咋办呢?? 包一层呗
164              */
165             //Task.Run(()=> {
166             //    ParallelOptions parallelOptions = new ParallelOptions();//这个可以控制并发数量(即线程数量)
167             //    parallelOptions.MaxDegreeOfParallelism = 3;//最多3个线程的要求    在这里可以实现
168             //    Parallel.For(0, 10, parallelOptions, i => Console.WriteLine("高薪就业"));
169             //});
170             #endregion
171 
172             #region   第五部分  临时变量
173             //for (int i = 0; i < 5; i++)
174             //{
175             //    Task.Run(()=>{
176             //        Thread.Sleep(1000);
177             //        Console.WriteLine(i);
178             //    });
179             //}
180             /*
181              分析上边的输出情况:   应该是每隔1秒钟,输出一个数字分别是  0,1,2,3,4
182             但是实际执行结果确实55555......理想丰满,显示骨感,,,怎么回事>>>????
183 
184             首先分析为什么打印出来全是5????
185                     因为计算机的执行速度是很快的,,,我再sleep(1000)后,i早就变成5了   所以会打印出5,
186                             假如没有sleep也会是5怎么解释?还是太快了,开线程的同时,早就i变为5了
187                                     因为全程只有1个i;
188             想要打印出01234的效果要做如下操作:  这样会有5个k,那么就可以打印出5个不同的数字了
189              */
190             //for (int i = 0; i < 5; i++)
191             //{
192             //    int k = i;
193             //    Task.Run(() => {
194             //        Thread.Sleep(1000);
195             //        Console.WriteLine(k);
196             //    });
197             //}
198             #endregion
199 
200 
201             #region  第六部分 线程安全  lock  重点
202             /*概念刨析:
203                     共有变量:  都能访问局部变量/全局变量/数据库得一个值/硬盘文件
204              线程内部不共享是  安全的  ;  共享就会有问题
205 
206                 上边说明了个什么问题??
207             线程安全的问题:
208                 1.如果所有线程都能访问到,那么这个变量或者实例就是线程不安全的,会发生丢包
209                 2.如果  变量是在线程内部,  那么就是线程安全的   不会发生丢包
210             也有坏处:============
211                 虽然解决了丢包的问题  ,  但是就不能并发了  因为每次只能一个线程去执行  .  牺牲了性能   要尽量缩小lock的范围
212              */
213             //例子:  
214 
215 
216             List<Task> taskList = new List<Task>();
217             List<int> intList = new List<int>();
218             int totalCount = 0;
219             //for (int i = 0; i < 10000; i++)
220             //{
221             //    int newI = i;
222             //    taskList.Add(Task.Run(()=> {
223             //        totalCount += 1;
224             //        intList.Add(newI);
225 
226             //    }));
227             //}
228             Console.WriteLine("=======加锁前后=======");
229 
230             for (int i = 0; i < 10000; i++)
231             {
232                 int newI = i;
233                 taskList.Add(Task.Run(() =>
234                 {
235                     /*  
236                         lock后的方法快,任意时刻只能有一个线程进入
237                         锁只能锁引用类型,  占用这个引用类型的 引用连接    但是不要用string  因为享元
238                      */
239                     lock (myLock)//   加锁后,会输出完整的数
240                     {
241                         totalCount += 1;
242                         intList.Add(newI);
243                     }
244 
245 
246                 }));
247 
248             }
249 
250             Console.WriteLine(totalCount);//输出9995
251             Console.WriteLine(intList.Count);//输出9996   说明这2个都会丢包
252             #endregion
253             Console.ReadKey();
254         }
255     }
256 }

 

标签:一次,Console,WriteLine,Thread,tasks,Task,线程,测试,多线程
来源: https://www.cnblogs.com/messi-10/p/16370067.html