概念
为防止一个应用程序控制CPU而导致其他程序或操作系统被永久挂起,操作系统采用某种方式将物理计算单元分割为一些虚拟进程,给予每个程序一点量的计算能力。且,操作系统始终能优先访问CPU,并能调整每个程序访问CPU的优先级。线程基于这一概念实现。可以认为线程是一个虚拟进程,用于独立运行一个特定的程序。
更多内容
线程会消耗大量操作系统资源。多个线程共享一个物理处理器将导致操作系统忙于管理线程,而无法运行程序。
线程生命周期:创建线程、挂起线程、线程等待以及终止线程。
需要的using
using System.Threading;
using static System.Threading.Thread;
创建线程、开启线程
Thread t = new Thread(FunName);//传递一个方法
t.Start();
进程
正在执行中的程序示例可被称为一个进程。进程由一个或多个线程组成。
暂停线程
让一个线程等待一段时间而不用消耗操作系统资源。
Thread.Sleep(TimeSpan.FromMilliseconds(2000));
线程等待
主程序调用了t.Join方法,该方法允许我们等待直到线程t完成,线程t完成后主程序继续允许。借助该技术可以实现两个线程间同步执行步骤:第一个线程会等待另一个线程完成后再继续执行,第一个线程等待时处于阻塞状态(与Thread.Sleep一样)。
Thread t = new Thread(PrintNumsWithDelay);
t.Start();
t.Join();
PrintNums();
终止线程
调用t.Abort方法终止线程,这个线程注入了ThreadAbortException方法,导致线程被终结。这很危险,因为该异常可以在任何时刻发生并可能彻底摧毁应用程序;且该技术也不一定总能终止线程,目标线程可以通过处理该异常并调用Thread.ResetAbort方法来拒绝被终止。因此不推荐使用Abort来终止线程,可以优先使用其他方法,比如提供一个CancellationToken方法来取消线程的执行。
Thread t = new Thread(PrintNumsWithDelay);
t.Start();
Thread.Sleep(TimeSpan.FromMilliseconds(2000));
t.Abort();
查看线程状态
刚开始线程状态为ThreadState.Unstarted,启动后ThreadState.Running,线程迭代一定次数后变为ThreadState.WaitSleepJoin,线程被终止后ThreadState.Aborted,也可能是ThreadState.AbortRequest,成功完成后变为ThreadState.Stopped。
t.ThreadState
注:通过Thread.CurrentThread静态属性获取当前Thread对象。
线程优先级
CurrentThread.Priority;//当前线程优先级
new Thread(FunName).Priority=ThreadPrioroty.Highest;//Lowest//设置程序优先级
注:限制程序单核运行
using static system.Diagnostics.Process;
{
//codes
GetCurrentProcess().ProcessorAffinity=new IntPtr(1);
}
前台/后台线程
线程默认是前台线程,通过设置IsBackground属性将其设置为后台线程。注:进程会等待所有前台程序完成后再结束工作,但如果剩下的全是后台线程,则会直接结束工作。
thread.IsBackground=true;
向线程传递参数
通过Thread.Start来传递方法参数,该方法接收单个object对象。
Thread t=new Thread(FunName);
t.Start(parameter);
FunName(object parameter)
{
//code…
}
使用C#中lock关键字
多个线程访问同一资源对象时,将会导致不确定的结果。这种情形被称为竞争条件(race condition)。竞争条件是多线程环境中非常常见的导致错误的原因。使用lock关键字来避免这种错误,如果锁定一个对象,需要访问该对象的所有其他线程则会处于阻塞状态,并等待直到该对象解除锁定,这可能导致严重的性能问题。
class Program { static void Main(string[] args) { Counter c = new Counter(); Thread t1 = new Thread(() => TestCounter(c)); Thread t2 = new Thread(() => TestCounter(c)); Thread t3 = new Thread(() => TestCounter(c)); t1.Start(); t2.Start(); t3.Start(); //t1.Join(); //t2.Join(); //t3.Join(); Console.WriteLine(c.Count); CounterWithLock c1 = new CounterWithLock(); Thread t4 = new Thread(() => TestCounter(c1)); Thread t5 = new Thread(() => TestCounter(c1)); Thread t6 = new Thread(() => TestCounter(c1)); t4.Start(); t5.Start(); t6.Start(); t4.Join(); t5.Join(); t6.Join(); Console.WriteLine(c1.Count); Console.ReadLine(); } static void TestCounter(CounterBase c) { for (int i = 0; i < 100000; i++) { c.Increment(); c.Decrement(); } } } class Counter : CounterBase { public int Count { get; private set; } public override void Decrement() { Count++; } public override void Increment() { Count--; } } class CounterWithLock : CounterBase { private readonly object _syncLock = new object(); public int Count { get; private set; } public override void Decrement() { lock (_syncLock) { Count++; } } public override void Increment() { lock (_syncLock) { Count--; } } } abstract class CounterBase { public abstract void Increment(); public abstract void Decrement(); }
Monitor类锁定资源
lock关键字可能创建死锁(deadlock),使用Monitor来避免死锁。
死锁:
lock(obj1) { //Code…… lock(obj2) { //Code…… } } lock(obj2) { //Code…… lock(obj1) { //Code…… } }
避免死锁:
lock(obj1) { //Code…… if(Monitor.TryEnter(obj2,TimeSpan.FromSeconds(5)))//避免死锁 { //Code…… } else { //Code…… } } lock(obj2) { //Code…… lock(obj1) { //Code…… } }
使用Monitor类的TryEnter方法,在超时参数过期前得不到所有的lock对象,则返回false,避免死锁出现。
注:本文是在阅读《C#多线程编程实战》后所写,部分内容引用该书内容,这是一本不错的书,感谢!