在CSharp中,使用Thread类进行创建和管理线程,通过这种方式创建的线程受CLR管理,由GC来分配和回收内存,因此也叫托管线程。
关于线程和进程:一个应用程序有至少一个进程;一个进程至少有一个线程。
进程与线程
同步与异步的区别
CSharp中的多线程
在C#中 Thread
类是.Net CLR对线程对象的抽象封装。多线程是操作系统提供的,Thread向操作系统申请线程。
异步多线程的特点
- 不会卡死主线程,更好的用户体验。
- 多个线程比单个线程处理速度快。系统资源换性能,非线性减少处理时间。
- 多个线程开始、结束 的先后顺序不固定。
BeginInvoke
method.Invoke() 主线程同步执行method方法。使用BeginInvoke()可以在子线程中异步调用方法,BeginInvoke()包含两个参数:返回一个IAsyncResult的结果。下面是如何处理异步的回调
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| int MainThreadID = Thread.CurrentThreadManagedThreadId; var MainThread = Thread.CurrentThread; Console.WriteLine($"主线程线程ID:{MainThreadID}"); Action test = () => { Console.WriteLine($"当前线程为:{Thread.CurrentThread.ManagedThreadId}"); };
IAsyncResult asyncResult = test.BeginInvok((state) => { Console.WriteLine($"异步执行结束,当前线程为:{Thread.CurrentThread.ManagedThreadId}"); }, null);
IAsyncResult asyncResult = test.BeginInvoke(null, null); while (!asyncResult.IsCompleted) { Thread.Sleep(100); } Console.WriteLine($"异步执行结束,当前线程为:{Thread.CurrentThread.ManagedThreadId}");
|
获取进程信息
1 2 3 4 5 6 7 8 9 10
| Thread thread = new Thread(() => { Console.WriteLine($"线程ID: {Thread.CurrentThread.ManagedThreadId}"); Console.WriteLine($"线程名称: {Thread.CurrentThread.Name}"); Console.WriteLine($"线程执行状态: {Thread.CurrentThread.IsAlive}"); Console.WriteLine($"是后台线程: {Thread.CurrentThread.IsBackground}"); Console.WriteLine($"在线程池中: {Thread.CurrentThread.IsThreadPoolThread}"); }); thread.Name = "子线程"; thread.Start();
|
创建线程
ThreadStart
ThreadStart 是一个无参委托。使用时无需传递参数,可以通过类的成员变量、静态变量传递信息。但是多线程时要进行加锁处理。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
|
static string url = "https://blog.tonychenn.cn"; static void Main(string[] args) { new Thread(Test).Start(); } static void Test() { Console.WriteLine("子线程请求URL"); var req = WebRequest.Create(url); WebResponse response = req.GetResponse(); string info = new StreamReader(response.GetResponseStream()).ReadToEnd(); Console.WriteLine(info); }
|
ParameterizedThreadStart
ParameterizedThreadStart 是一个带有一个object类型的委托。可以向线程内传递参数,但是object类型需要装箱拆箱。
1 2 3 4 5 6 7 8 9 10 11 12
| static void Main(string[] args) { Thread thread = new Thread(new ParameterizedThreadStart(Test)); thread.Start("https://blog.tonychenn.cn"); } static void Test(object obj) { Console.WriteLine("子线程请求URL"); var req = WebRequest.Create(obj as string); string info = new StreamReader(req.GetResponse().GetResponseStream()).ReadToEnd(); Console.WriteLine(info); }
|
Lambda
像上面 获取进程信息
中那样直接传递一个委托方便快捷。
线程状态与切换
1 2 3 4 5 6 7 8 9 10 11 12 13
| public enum ThreadState { Running = 0x0, StopRequested = 0x1, SuspendRequested = 0x2, Background = 0x4, Unstarted = 0x8, Stopped = 0x10, WaitSleepJoin = 0x20, Suspended = 0x40, AbortRequested = 0x80, Aborted = 0x100 }
|

线程的休眠与阻塞
使用 Thread.Sleep()
、Thread.Yield()
可以将线程挂起一段时间。
Thread.Join()
方法可以 阻塞当前进程一直等到另一个线程运行结束。
在 Join 或 Sleep 过程中,线程是阻塞的。
阻塞的定义
当线程由于特定原因暂停执行,那么它就是阻塞的。如果线程处于阻塞状态,线程就会交出他的 CPU 时间片,并且不会消耗 CPU 时间,直至阻塞结束,即线程会阻塞,但是 CPU 会继续找到其他未阻塞的线程继续执行指令,避免 CPU 空闲。阻塞会发生 CPU 从一个线程换到另一个线程执行,即上下文切换。
- 线程会放弃自己的CPU时间,将CPU交给其他线程执行。即使没有其他就绪的线程也不会占用CPU时间。
- 可以调度任何处理器的线程使用时间片。
- 线程会让出自己的CPU时间,但是当没有其他就绪的线程时,它会继续执行。
- 只能调度运行在当前CPU的线程。
- Thread.Join
用来阻塞线程,阻塞的线程只有执行完毕后才会继续执行其他线程(包括主线程)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| static void Main() { Console.WriteLine("AAA"); var thread = new Thread(Test); thread.Start(); thread.Join(); Console.WriteLine("BBB"); }
public static void Test() { for (int i = 0; i < 10; i++) { Console.Write(i); Thread.Sleep(100); } }
|
线程的终止与恢复
- 通过
Abort()
方法来强制停止正在执行的线程。他会抛出一个ThreadAbortException
的异常从而终止线程。
- 在抛出
ThreadAbortException
异常中,线程还未结束,可以通过ResetAbort()
方法从而恢复终止的线程。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| static void print() { FLAG: try{ for (int i = 0; i < 20; i++) { Console.Write(i); Thread.Sleep(100); } } catch (ThreadAbortException e) { Console.WriteLine("子线程要终止了"); Thread.ResetAbort(); Console.WriteLine("子线程又恢复了"); goto FLAG; } Console.WriteLine("子线程执行完毕"); } static void Main(string[] args) { Thread thread = new Thread(print); thread.Start(); Task.Delay(300).Wait(); thread.Abort(); }
|
线程的优先级
1 2 3 4 5 6 7 8
| public enum ThreadPriority { Lowest, BelowNormal, Normal, AboveNormal, Highest }
|
可以通过 Thread.IsBackground 方法可以设置是否为后台线程。前台线程的优先级是高于后台线程的。
线程优先级过大可能会占用大量CPU时间导致系统无响应或卡顿。线程优先级过低会导致过少CPU时间占用导致窗口无相应。
优先级越高分配的CPU时间越多,优先级越低分配CPU时间越少。
处理线程的异常
只能在线程内部try/catch异常。