测试一次多线程
作者:互联网
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