最近花时间了解了下.NetFramework的多线程,看了一个基础教程https://www.bilibili.com/s/video/BV1TJ411v7T7
教程中对多线程的概念做了基础讲解,并且也有提到.NetFramework的多线程发展,如果对多线程不是很了解或者说想全面了解这块知识的也可以去看一下,内容相对简单。
* 下面对我看这个视频的收获做一个总结。
* 概念:
- 线程是进程的一个实体,是CPU调度和分配的基本单位,它比进程更下能独立运行的基本单位,线程自己基本上不拥有系统资源,只拥有一点在运行中不可少的资源(如程序计数器、栈),但是它可与同属一个进程的其他线程共享进程所拥有的全部资源。
- 进程(Process)是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。 在早期面向进程设计的计算机结构中,进程是程序的基本执行实体;在当代面向线程设计的计算机结构中,进程是线程的容器。
- 多线程:一个进程中,多个线程并发执行。
* 发展时间线
- 在.NetFramework1.0,1.1版本时,用的是Thread,Thread的API相当丰富,但是使用不方便(视频中提及),因为线程资源是操作系统管理的,响应并不灵敏,并不好控制。
- ThreadStart threadStart = ()=>{};
- Thread thread = new Thread(threadStart);
- 到了.NetFramework2.0(新的CLR),提出ThreadPool,池化管理设计思想。API少,线程等待顺序控制特别弱,ManualResetEvent,影响实战。
- WaitCallback callback = p = >{};
- ThreadPool.QueueUserWorkItem(callback);
- 到了.NetFramework3.0,提出Task,Task被称为多线程的最佳实践。
- Task是线程全部是线程池线程。提供了丰富的API.
* Action action = ()=>{ };
Task task = new Task(action);
task.Start();
List<Task> taskList = new List<Task>();
taskList.Add(Task.Run(()=>{Console.WriteLine("1");}));
taskList.Add(Task.Run(()=>{Console.WriteLine("2");}));
Task.WaitAll(taskList.ToArray());//阻塞式调用
TaskFactory taskFactory = new TaskFactory();
taskFactory.ContinueWhenAll(taskList.ToArray(), tArray =>{
})
var task = taskFactory.ContinueWhenAny(taskList.ToArray, tArray =>{
})
启动一个线程去做后面的事情,task是线程,线程是线程池里面的,可能是刚结束的线程,不可能是主线程。
Task初印象,使用方便,对于线程的顺序控制比较方便。Task这块还需要再花点时间全面了解一下。
还有Parallel也是能启动子线程。
* 线程安全:
线程安全出现在修改对象时,如果一段代码单线程和多线程的执行结果不一样,就表明线程安全存在问题。
加lock能解决线程安全问题,就是单线程化,Lock保证方法块任意时刻只有一个线程能进去。
lock是个语法糖,类似于Monitor,锁定一个内存引用类型,不能是值类型,也不能是null(null编译能通过,但是执行有问题)。
private static readonly Object Lock = new Object();
Monitor.Enter(LOCK)
try{
DO();
}
finally{
Monitoe.Exit(LOCK)
}
等价于
lock(LOCK)
{
DO();
}
只在主线程中lock并不会造成死锁。
*await/async
await/async 是个新语法,出现在C#5.0 .NetFramework4.5及以上(CLR)
只是一个语法糖,不是一个全新的异步多线程使用方式。
本省并不会产生新的线程,依托于task存在。
async可以随意添加,出现await的时候必须添加async,不能单独出现。
public static void Main()
{
Console.WriteLine(“AsynBefore”);
var t = AsyncMethod();
Console.WriteLine(“AsynBegin”);
Console.WriteLine(t.Result);
Console.WriteLine(“AsynAfter”);
}
public static async Task AsyncMethod()
{
Console.WriteLine(“Func Start”);
Task task1 = Task.Run(() =>
{
Console.WriteLine(KaTeX parse error: Expected 'EOF', got '}' at position 83: …eep(1000); }̲); await ta…“Func Mid {Thread.CurrentThread.ManagedThreadId}”);
Task task2 = Task.Run(() =>
{
Console.WriteLine(KaTeX parse error: Expected 'EOF', got '}' at position 82: …eep(1000); }̲); await ta…“Func End {Thread.CurrentThread.ManagedThreadId}”);
return 666;
}
输出为
AsynBefore
Func Start
AsynBegin
Task1 3
Func Mid 3
Task2 4
Func End 4
666
AsynAfter
解析:首先是主线程输出 AsynBefore, 然后进到函数还是主线程输出Func Start,这时候启线程去跑task1,task1还没开始返回主线程输出AsynBegin,这时候t.Result是阻塞方法,
主线程阻塞,进程到子线程里面输出Task1和FuncMid,又启动一个线程输出Task2和FuncEnd,这时候将666返回主线程,主线程将666输出。
await/async的好处是以同步编程的方式进行异步编码。