我们习惯在一个单独的线程中额外执行一些耗时,或者执行一些可能妨碍其他任务的任务的操作,通过多个线程的合理运用,让客户端得到更为快速的响应方式。通过利用单独的线程去执行 CPU 密集型和 IO 密集型的工作,让用户界面一直处于活跃状态。当然,任何事物都具有两面性,处理不当的话会诱发线程安全的问题。
创建与使用线程
我们常使用 new 关键字对线程实现创建,下面是一些常见的线程方法。
//创建线程
var thread = new Thread(() => Console.WriteLine());
//启动
thread.Start();
//终止
thread.Abort();
#region 方法已过时
//挂起
thread.Suspend();
//恢复挂起的线程
thread.Resume();
#endregion 方法已过时
线程属性
一些常见的属性。
//线程优先级
//thread.Priority
//线程的状态
//thread.ThreadState
//线程的执行状态
//thread.IsAlive
//是否后台线程
//thread.IsBackground
//线程名称
//thread.Name
线程的优先级
CPU 会向优先级高的线程分配更长的时间段。
后台线程
后台线程会在最后一个前台线程停止后立即停止。
多线程的参数和返回值
在线程中调用含参的方法
假如我们希望在线程中调用一个包含参数的方法的时候,会如何去调用呢?
这里提供的思路是,新建一个类,把方法和参数进行分离,参数作为类的属性成员,方法直接对属性进行访问操作即可。
获取线程中的返回值
通过 BackWorker 进行通信。
线程同步
多线程的优点:每个线程都可以以异步的方式去执行。对窗体程序,可以在后台执行耗时的任务,UI 界面和控件始终保持响应;而服务器程序,每个请求都使用一个线程进行处理。
在进行多线程对 IO、网络连接和内存等资源进行访问时,要注重协调性。因为线程在执行的时候它不能够感知到其他线程的执行状态,当多个线程在同一时间对同一资源进行访问时,就可能出现无法预知的状态结果。
lock
lock,这应该是保证线程安全最常用的一个关键字。lock 要求的对象必须是引用类型,该对象的作用就是标识这块区域将会被多个线程进行使用,当一个线程进入这片区域后,这块区域就变成锁定的状态,其它线程都在等待中,直到进入该区域的线程执行完毕(跳出 lock 的区域)。这种行为我们认为是安全同步的。
使用 lock 有几个比较重要的知识点:
(1)避免使用 public 类型。如 lock(this) 会出现死锁,即多个线程会等待同一个对象实例的释放;
(2)避免使用 string 类型(字符串)。因为它会被 CLR 暂存,任何一个特定的字符串都只有一个实例,意味着在不同的地方使用相同内容的字符串,其实它们(字符串)都指向的是同一个实例对象;
(3)建议是私有或受保护的成员。
Monitor
【参考】https://docs.microsoft.com/zh-cn/dotnet/csharp/programming-guide/concepts/threading/multithreaded-applications