??在单核计算机上,线程调度器会进行 时间切片(time-slicing) ,快速的在活动线程中切换执行 。在 Windows 操作系统上,一个时间片通常在十几毫秒(译者注:默认 15.625ms),远大于 CPU 在线程间进行上下文切换的开销(通常在几微秒区间) 。
??在多核计算机上,多线程的实现是混合了时间切片和 真实的并发(genuine concurrency),不同的线程同时运行在不同的 CPU 核心上 。仍然会使用到时间切片,因为操作系统除了要调度其它的应用,还需要调度自身的线程 。
??线程的执行由于外部因素(比如时间切片)被中断称为 被抢占(preempted) 。在大多数情况下 , 线程无法控制其在什么时间 , 什么代码块被抢占 。
??多线程同样也会带来缺点,最大的问题在于它提高了程序的复杂度 。使用多个线程本身并不复杂,复杂的是线程间的交互(共享数据)如何保证安全 。无论线程间的交互是否有意为之,都会带来较长的开发周期,以及带来间歇的、难以重现的 bug 。因此,最好保证线程间的交互尽可能少,并坚持简单和已被证明的多线程交互设计 。三、基础创建与启动使用Thread类的构造方法来创建线程,支持以下两种委托
??当频繁地调度和切换线程时(且活动线程数量大于 CPU 核心数),多线程会增加系统资源和 CPU 的开销,线程的创建和销毁也会增加开销 。多线程并不总是能提升程序的运行速度,如果使用不当 , 反而可能降低速度 。
public delegate void ThreadStart();public delegate void ParameterizedThreadStart (object? obj);
关于Thread构造重载方法参数 maxStackSize,不建议使用https://stackoverflow.com/questions/5507574/maximum-thread-stack-size-net
public void 创建一个线程(){var t = new Thread(Go);// 开一个线程tt.Start();// 启动t线程,执行Go方法Go();// 主线程执行Go方法}void Go(){_testOutputHelper.WriteLine("hello world!");}
每一个线程都有一个 Name 属性,我们可以设置它以便于调试 。线程的名字只能设置一次,再次修改会抛出异常 。public void 线程命名(){var t = new Thread(Go);// 开一个线程tt.Name = "worker";t.Start();// 启动t线程,执行Go方法Go();// 主线程执行Go方法}void Go(){// Thread.CurrentThread属性会返回当前执行的线程_testOutputHelper.WriteLine(Thread.CurrentThread.Name + " say: hello!");}
传递参数Thread类的Start方法重载支持向thread实例传参public void Start(object? parameter)
参数被lambda表达式捕获,传递给Go方法public void 创建一个线程(){var t = new Thread(msg => Go(msg));// 开一个线程tt.Start("hello world!");// 启动t线程,执行Go方法Go("main thread say:hello world!");// 主线程执行Go方法}void Go(object? msg){_testOutputHelper.WriteLine(msg?.ToString());}
请务必注意,不要在启动线程之后误修改被捕获变量(captured variables)public void 闭包问题(){for (int i = 0; i < 10; i++){new Thread (() => Go(i)).Start();}}
前台/后台线程默认情况下,显式创建的线程都是前台线程(foreground threads) 。只要有一个前台线程在运行,程序就可以保持存活不结束 。当一个程序中所有前台线程停止运行时,仍在运行的所有后台线程会被强制终止 。这里说的 显示创建 , 指的是通过new Thread()创建的线程当进程以强制终止这种方式结束时,后台线程执行栈中所有finally块就会被避开 。如果程序依赖finally(或是using)块来执行清理工作,例如释放数据库/网络连接或是删除临时文件,就可能会产生问题 。为了避免这种问题,在退出程序时可以显式的等待这些后台线程结束 。有两种方法可以实现:
非默认情况,指的是将Thread的IsBackground属性设置为true
static void Main (string[] args){ Thread worker = new Thread ( () => Console.ReadLine() ); if (args.Length > 0) worker.IsBackground = true; worker.Start();}
- 如果是显式创建的线程,在线程上调用Join阻塞 。
- 如果是使用线程池线程,使用信号构造 , 如事件等待句柄 。
线程的 前台/后台状态 与它的 优先级/执行时间的分配无关 。异常处理当线程开始运行后,其内部发生的异常不会抛到外面,更不会被外面的try-catch-finally块捕获到 。
推荐阅读
- 云原生之旅 - 11)基于 Kubernetes 动态伸缩 Jenkins Build Agents
- go GMP
- iphone13出来12会降价吗_iphone13出来12能便宜多少
- 苹果13mini屏幕多大尺寸_苹果13mini屏幕尺寸
- 618红米k40pro会降价吗_618红米k40pro能便宜多少
- 我的世界漏斗与漏斗之间怎么相连(我的世界漏斗上怎么放东西)
- 洛谷 P4135 作诗 题解
- 明日方舟泳装皮肤多少源石头
- 电脑垫多高颈椎最舒服_显示器抬高多少厘米合适
- iqoo7支持无线充电吗_iqoo7电池容量是多少