一 什么是多线程
1, 什么是进程? 一个 exe 运行一次就会产生一个进程, 一个 exe 的多个进程之 间数据互相隔离.
2, 一个进程里至少有一个线程: 主线程. 我们平时写的控制台程序默认就是单线程的, 代 码从上往下执行, 一行执行完了再执行下一行;
3, 什么是多线程: 一个人两件事情同时做效率高. 同一时刻一 个人只能干一件事情, 其实是在 "快速频繁切换", 如果处理不当可能比不用多线程效率还低
二 Thread 对象
2.1 thread 基础写法
- public static void ThreadTest()
- {
- int a = 0;
- Thread thread1 = new Thread(m=>
- {
- for (int i = 0; i <20; i++)
- {
- a = a + 1;
- Console.WriteLine("线程 1:"+ a);
- }
- });
- Thread thread2 = new Thread(m =>
- {
- for (int i = 0; i <20; i++)
- {
- a = a + 1;
- Console.WriteLine("线程 2:"+ a);
- }
- });
- thread1.Start();
- thread2.Start();
- Console.ReadKey();
- }
这段代码输出结果如下:
可以看出两个子线程启动后是并行执行的, 所以输出结果没有按照顺序来
2.2 设置线程的优先级
thread1.Priority=ThreadPriority...
2.3 t1.Join()当前线程等待 t1 线程执行结束, 实例如下:
这段代码执行过后输出的结果就是正常的从 1 输出到了 40
- public static void ThreadTest()
- {
- int a = 0;
- Thread thread1 = new Thread(m=>
- {
- for (int i = 0; i <20; i++)
- {
- a = a + 1;
- Console.WriteLine("线程 1:"+ a);
- }
- });
- Thread thread2 = new Thread(m =>
- {
- // 等待 thread1 线程任务完成后在执行
- thread1.Join();
- for (int i = 0; i <20; i++)
- {
- a = a + 1;
- Console.WriteLine("线程 2:"+ a);
- }
- });
- // 可以将参数传入到子线程中
- thread1.Start(a);
- //thread1.Join(); 或者将 Join 放在这里
- thread2.Start(a);
- Console.ReadKey();
- }
2.4 Interrupt 方法
Interrupt 用于提前唤醒一个在 Sleep 的线程, Sleep 方法会抛出 ThreadInterruptedException 异常 代码如下:
代码输出到 9 的时候线程会休眠 8 秒钟, 但是运行到主线程 thread1.Interrupt()时, 子线程会被唤醒, 然后执行 catch 里面的 Console.WriteLine("线程被唤醒"); 之后接着从 10 开始输出到 2000. 需要注意的是只有线程自身能让自身休眠
- public static void ThreadTest2()
- {
- Thread thread1 = new Thread(() =>
- {
- for (int i = 0; i <2000; i++)
- {
- if (i==10)
- {
- // 唤醒线程之后会引发 ThreadInterruptedException 类型的异常, 所以需要 try catch
- try
- {
- // 子线程休眠 8 秒钟
- Thread.Sleep(8000);
- }
- catch (ThreadInterruptedException ex)
- {
- Console.WriteLine("线程被唤醒");
- }
- }
- Console.WriteLine(i);
- }
- });
- thread1.Start();
- // 提前唤醒在沉睡的子线程
- Thread.Sleep(3000);
- thread1.Interrupt();
- Console.ReadKey();
- }
三 线程池
3.1, 线程池: 因为每次创建线程, 销毁线程都比较消耗 cpu 资源, 因此可以通过线程池进行 优化. 线程池是一组已经创建好的线程, 随用随取, 用完了不是销毁线程, 然后放到线程池 中, 供其他人用.
3.2, 用线程池之后就无法对线程进行精细化的控制了(线程启停, 优先级控制等)
3.3,ThreadPool 类的一个重要方法:
- static bool QueueUserWorkItem(WaitCallback callBack)
- static bool QueueUserWorkItem(WaitCallback callBack, object state)
3.4, 除非要对线程进行精细化的控制, 否则建议使用线程池, 因为又简单, 性能调优又更好.
- //QueueUserWorkItem 是一个静态方法不需要 New
- public static void ThreadPool()
- {
- System.Threading.ThreadPool.QueueUserWorkItem(m=>
- {
- for (int i = 0; i <1000; i++)
- {
- i++;
- Console.WriteLine(i);
- }
- });
- Console.ReadKey();
- }
四 TPL 风格的异步方法
TPL(Task Parallel Library)是. Net 4.0 之后带来的新特性, 更简洁, 更方便. 现在在. Net 平台下已经大面积使用.
注意方法中如果有 await, 则方法必须标记为 async, 不是所有方法都可以被轻松的标记 为 async.WinForm 中的事件处理方法都可以标记为 async,MVC 中的 Action 方法也可以标 记为 async, 控制台的 Main 方法不能标记为 async. TPL 的特点是: 方法都以 XXXAsync 结尾, 返回值类型是泛型的 Task<T>. TPL 让我们可以用线性的方式去编写异步程序, 不再需要像 EAP 中那样搞一堆回调, 逻 辑跳来跳去了.
/TPL 风格返回的 Task<T> 泛型的数据
- //await 关键字等待异步方法返回
- public static async void Task()
- {
- webClient wc = new WebClient();
- string s= await wc.DownloadStringTaskAsync("http://www.baidu.com");
- Console.WriteLine(s);
- Console.ReadKey();
- }
- public static void Task2()
- {
- WebClient wc = new WebClient();
- // 若果不使用 await 关键字就得使用 Task<string > 类型来接收数据
- Task<string> s2 = wc.DownloadStringTaskAsync("http://www.baidu.com");
- Console.ReadKey();
- }
自己编写一个 TPL 风格的异步方法:
使用了 async 关键字就必须返回 Task 泛型数据类型的数据
- public static Task<string> StringAsync()
- {
- return Task.Run(() =>
- {
- Thread.Sleep(5000);
- return "hehe";
- });
- }
- // GET: Home
- public async Task<ViewResult> Index()
- {
- var s = await StringAsync();
- return View();
- }
如果返回值就是一个立即可以随手可得的值, 那么就用 Task.FromResult() 如果是一个需要休息一会的任务 (比如下载失败则过 5 秒钟后重试. 主线程不休息, 和 Thread.Sleep 不一样), 那么就用 Task.Delay(). 3,Task.Factory.FromAsync() 把 IAsyncResult 转换为 Task, 这样 APM 风格的 api 也可以用 await 来调 用. 4, 编写异步方法的简化写法. 如果方法声明为 async, 那么可以直接 return 具体的值, 不再用创建 Task, 由编译器创建 Task:
- static async Task<int> F1Async()
- {
- return 1;
- }
- static Task<int> F2Async()
- {
- return Task.FromResult(3);
- }
- static Task<int> F3Async()
- {
- return Task.Run(()=> {
- return 1 + 3; });
- }
一定要让 async 的传染性 (调用异步方法要用 await, 用了 await 方法就要声明为 async, 调 用我这个 async 方法的地方必须要 await......) 不要轻易直接调用 Task 的 Wait,WaitAll 等方 法. 等待一个用 await, 而不是 task.Wait(); 等待多个用 await Task.WhenAll(), 而不是 Task.WaitAll()
来源: https://www.cnblogs.com/xiongT/p/8988442.html