当前位置: 首页 > news >正文

c#基础6


进程与线程的一个简单解释 - 阮一峰的网络日志 进程和线程的理解

多线程编程

进程 主线程和分线程关系

一个程序就是一个进程,然而进程里面包含若干个线程,而每个进程里面都有一个(可以说必须要有一个)线程,这个线程就是主线程,然而主线程有一天发现自己的工作太多了,在规定的时间内完不成工作,这时候他就召唤了一个小弟(子线程)帮他,他给小弟分配了一些任务,当小弟做完了分配给他的任务后,他就把小弟赶走了!这就是主线程和子线程。

//

有4种创建线程的方式:

  • 1.Thread 自己创建的独立的线程, 优先级高,需要使用者自己管理。

//创建线程1 //创建分线程执行哪个方法 ThreadStart childref = new ThreadStart(CallToChildThread); //创建分线程实例对象 Thread childThread = new Thread(childref); //执行分线程 childThread.Start(); public static void CallToChildThread() { while (true) { Console.WriteLine("执行繁重的任务"); } } // 简写 Thread childThread = new Thread(() => { while (true) { Console.WriteLine("执行繁重的任务"); } }); //线程暂停2 //创建分线程执行哪个方法 ThreadStart childref = new ThreadStart(CallToChildThread); //创建分线程实例对象 Thread childThread = new Thread(childref); // 设置线程的名字 childThread.Name = "分线程1"; //执行分线程 childThread.Start(); public static void CallToChildThread() { //线程休眠 当把方法写在哪个线程中就休眠哪个线程 Thread.Sleep(3000); while (true) { Console.WriteLine("执行繁重的任务"); } } //线程销毁 1.线程方法Method执行完结,线程自动销毁 2.如果是无限循环需要手动销毁 //创建线程 //创建分线程执行哪个方法 ThreadStart childref = new ThreadStart(CallToChildThread); //创建分线程实例对象 Thread childThread = new Thread(childref); childThread.Name = "分线程1"; //执行分线程 childThread.Start(); //线程休眠 当把方法写在哪个线程中就休眠哪个线程 Thread.Sleep(3000); //销毁线程 childThread.Abort(); Console.WriteLine("haha"); } public static void CallToChildThread() { while (true) { Console.WriteLine("111111"); } } 线程函数通过委托传递,可以不带参数,也可以带参数(只能有一个参数) Thread t2 = new Thread(new ParameterizedThreadStart(TestMethod)); t2.Start("hello"); public static void TestMethod(object data) { string datastr = data as string; Console.WriteLine("带参数的线程函数,参数为:{0}", datastr); } //简写 Thread t2 = new Thread((e) => { Console.WriteLine("带参数的线程函数,参数为:{0}", e); }); t2.Start("hello"); 线程阻塞 Join() 线程之间执行顺序默认是无关的 为了保持同步 使用join() Thread thread1 = new Thread(() => { Console.WriteLine("2"); }); Thread thread2 = new Thread(() => { //线程同步 thread1.Join(); Console.WriteLine("3"); }); Thread thread3 = new Thread(() => { thread2.Join(); Console.WriteLine("4"); }); thread1.Start(); thread2.Start(); thread3.Start(); 线程抢占 如果两个线程同时对某个资源进行同时访问 就可能出现 线程抢占 static bool done; static void Main(string[] args) { new Thread(Go).Start(); Go(); Console.ReadKey(); } static void Go() { if (!done) { Console.WriteLine("Done"); done = true; } } 解决线程抢占问题 使用线程锁 static readonly object locker = new object(); //线程锁对象 static bool done; static void Main(string[] args) { new Thread(Go).Start(); Go(); Console.ReadKey(); } static void Go() { //lock关键字 线程锁 (唯一的对象) lock (locker) { if (!done) { Console.WriteLine("Done"); done = true; } } } 前台线程与后台线程 前台线程会随着主线程窗口关闭而停止,后台线程即使主线程窗口关闭自己独立运行。 IsBackground 设置是否是前后台线程 Thread worker = new Thread(() => Console.ReadLine()); worker.IsBackground = true; worker.Name = "backThread"; worker.Start(); Console.WriteLine("finish!");
  • 2.ThreadPool 线程池

  • 线程和线程池都是进行多线程操作的,线程池是用来保存线程的一个容器,在程序创建线程来执行任务的时候线程池才会初始化一个线程,线程在执行完毕之后并不会被销毁,而是被挂起等待下一个任务的到来被激活执行任务,当线程池里的线程不够用的时候会新实例化一个线程,来执行,线程池里的线程会被反复利用

方式一: QueueUserWorkItem 接收一个参数,参数类型是WaitCallback 它是一个无返回值委托,委托函数接收一个obejct类型参数。 官方定义:public delegate void WaitCallback(object state); WaitCallback callBack = DoSomething; ThreadPool.QueueUserWorkItem(callBack); static void DoSomething(object obj) { Console.WriteLine("do something"); } 以上代码可以简写, waitCallback委托赋值一个匿名方法 WaitCallback waitCallback = arg => Console.WriteLine("dosomething1"); ThreadPool.QueueUserWorkItem(waitCallback) 继续简写 ThreadPool.QueueUserWorkItem(e => { Console.WriteLine("do something2"); }); 方式二: QueueUserWorkItem 接收两个参数,第一个参数是WaitCallback 委托类型,第二个参数是object对象,用于传递给委托函数 参数"dosomething3"将传递给委托函数DoSomething static void DoSomething(object value) { Console.WriteLine(value); } WaitCallback waitCallback1 = DoSomething; ThreadPool.QueueUserWorkItem(waitCallback1, "dosomething3"); 以上代码简写 使用匿名函数进行简写: ThreadPool.QueueUserWorkItem(e => { DoSomething(e); }, "dosomething4"); 线程休眠 //ManualResetEvent mreset = new ManualResetEvent(false); ThreadPool.QueueUserWorkItem(e => { Thread.Sleep(2000); Console.WriteLine("dosomething"); // mreset.Set(); }); Console.WriteLine("dosomething else..."); Console.WriteLine("dosomething else..."); //阻塞主线程 等待分线程完成后 执行mreset.Set()后执行后续代码 mreset.WaitOne();
  • 3.net4.0在ThreadPool的基础上推出了Task类

  • ThreadPool不支持线程控制,线程延续 ,线程销毁
  • task 解决了 以上问题

1.Task位于using System.Threading.Tasks;命名空间下

2.Task和ThreadPool一样 都是线程池操作

创建Task

//方式1 //第一种创建方式,直接实例化:必须手动去Start 可以绑定有参数的委托对象 var task1 = new Task(() => { //TODO you code }); task1.Start(); //方式2 //第二种创建方式,工厂创建,直接执行 且绑定的都是无参无返回值的委托对象 var task2 = Task.Factory.StartNew(() => { }); 或者是 Task.Run(() =>{ });
三、Task的任务控制:Task比threadPool优点就是任务控制,很好的控制task的执行顺序,让多个task有序的执行 Task.Wait task1.Wait();就是等待任务执行(task1)完成,task1的状态变为Completed。 Task.WaitAll 待所有的任务都执行完成: Task.WaitAny 等待任何一个任务完成就继续向下执行 Task.ContinueWith 第一个Task完成后自动启动下一个Task,实现Task的延续 CancellationTokenSource 通过cancellation的tokens来取消一个Task。 // 1.task1.Wait() Task task = Task.Run(() => { Thread.Sleep(3000); Console.WriteLine("1"); }); 等待任务执行(task)完成 后执行后续代码 task.Wait(); Task task1 = Task.Run(() => { Console.WriteLine("2"); }); Task task2 = Task.Run(() => { Console.WriteLine("3"); }); Console.WriteLine("All task finished!"); //2.Task.WaitAll 待所有的任务都执行完成 Task task = Task.Run(() => { Thread.Sleep(3000); Console.WriteLine("1"); }); Task task1 = Task.Run(() => { Console.WriteLine("2"); }); Task task2 = Task.Run(() => { Console.WriteLine("3"); }); Task.WaitAll(task, task1, task2); 待所有的任务都执行完成 执行以下内容 Console.WriteLine("All task finished!"); // 3.Task.WaitAny 等待任何一个任务完成就继续向下执行 Task task = Task.Run(() => { Thread.Sleep(3000); Console.WriteLine("1"); }); Task task1 = Task.Run(() => { Console.WriteLine("2"); }); Task task2 = Task.Run(() => { Console.WriteLine("3"); }); //等待其中任意一个任务完成后 执行后续代码 Task.WaitAny(task, task1, task2); Console.WriteLine("All task finished!"); 4. Task.ContinueWith 线程延续 :如果线程中的结果 需要再后续使用 使用线程延续 //线程延续 Task<int> task8 = Task<int>.Run(() => { //执行繁重的任务 return 8; }); Task<int> task9 = task8.ContinueWith<int>(a => { int b = 10; Console.WriteLine("task9线程延续的结果:" + ( a.Result + b)); return a.Result + b; }); Task<int> task10 = task9.ContinueWith<int>(a => { int c = 10; Console.WriteLine("task10线程延续的结果:" + (a.Result + c)); return a.Result + c; });
task线程取消 方法 static void Main(string[] args) { //1.初始化线程取消类 var tokenSource = new CancellationTokenSource(); //2.获取线程取消标记 var token = tokenSource.Token; //3.开启task线程 并且绑定取消线程标记 var task = Task.Run(() => { for (var i = 0; i < 1000; i++) { Thread.Sleep(1000); //是否执行取消方法 如果取消 为true 反之为 false if (token.IsCancellationRequested) { Console.WriteLine("Abort mission success!"); return; } } }, token); //取消线程后回调方法 token.Register(() => { Console.WriteLine("Canceled"); }); Console.WriteLine("Press enter to cancel task..."); Console.ReadKey(); //取消线程方法 tokenSource.Cancel(); Console.ReadKey(); }

封装 task 暂停和重启 的管理类

using System.Threading; using System.Threading.Tasks; public class PausableTask { // 核心信号量:控制暂停/启动,初始为启动状态(Set) private readonly ManualResetEventSlim _pauseSignal = new ManualResetEventSlim(true); // 取消令牌:用于安全终止Task,避免阻塞 private CancellationTokenSource _cts; // 封装的可暂停Task public Task RunningTask { get; set; } /// <summary> /// 启动可暂停的Task /// </summary> /// <param name="action">Task执行的业务逻辑(需包含暂停检测)</param> public void Start(Action action) { if (RunningTask != null && !RunningTask.IsCompleted) { throw new InvalidOperationException("Task已在运行,请勿重复启动"); } // 重置取消令牌 _cts = new CancellationTokenSource(); // 启动Task,封装业务逻辑+暂停检测+取消检测 RunningTask = Task.Run( () => { try { while (!_cts.Token.IsCancellationRequested) { // 核心:检测暂停信号,未触发则阻塞(暂停),触发则继续 _pauseSignal.Wait(_cts.Token); // 执行业务逻辑 action.Invoke(); } } catch (OperationCanceledException) { // 捕获取消异常,正常退出(非错误) Console.WriteLine("Task被安全取消"); } }, _cts.Token); } /// <summary> /// 暂停Task(无阻塞,仅设置信号量) /// </summary> public void Pause() { if (_pauseSignal.IsSet) { _pauseSignal.Reset(); // 信号量未触发,Task会在下次Wait时阻塞 Console.WriteLine("Task已暂停"); } } /// <summary> /// 启动/恢复Task(无阻塞,仅设置信号量) /// </summary> public void Resume() { if (!_pauseSignal.IsSet) { _pauseSignal.Set(); // 信号量触发,阻塞的Task会继续执行 Console.WriteLine("Task已恢复"); } } }

使用封装的方法

//创建PausableTask对象 用于后续开启 暂停 线程操作 PausableTask pausableTask = new PausableTask(); // 启动Task pausableTask.Start(() => { //业务逻辑 }); //暂停线程 pausableTask.Pause(); //重启线程 pausableTask.Resume();

4.异步和多线程

异步侧重于任务的执行顺序,而多线程则是关于多个线程如何并发执行。应该说多线程是实现异步的常用手段,但不能说他们是一回事。即便是只有一个线程的情况下,我们仍然可以实现异步 使用async/await可以实现

异步:表示多个任务之间互不干扰,同步反之

1.async/await 概念

1. net5.0推出了async/await async/await特性是与Task紧密相关的

2.async 是“异步”的简写,sync 是“同步”的简写

await 是 async wait 的简写。await 用于等待一个异步方法执行完成

//如何通过使用async/await 完成异步编程

//1. async 必须修饰方法 被修饰的方法 表示是一个异步方法

//2.async 和await必须连用 如果不使用await 那么这个方法还是同步方法
//3.async 描述的方法 的返回值类型必须是void 或者是Task 或者Task<T>
//4.await 描述的也是方法 但是必须是使用线程(task)的方法
//5.Async方法在执行的时候,开始是以同步的方式执行,直到遇到await关键字,
//从await关键字开始,

异步执行 async方法之外的其他代码

同步执行 async方法中await关键字之后的代码

2.不使用用异步async/await完成下载
不使用async/await 完成下载逻辑,则代码执行顺序时混乱的,不符合业务逻辑: internal class Program { static void Main(string[] args) { DoSomething(); Console.WriteLine("方法外面代码也与Task异步执行"); Console.ReadLine(); } public static void DoSomething() { Console.WriteLine("开始下载"); //与Task线程之外的代码都是异步执行 //所以结束下载 在下载完成之前已经执行了 Task.Run(() => { Thread.Sleep(1000); Console.WriteLine("下载10%"); Thread.Sleep(1000); Console.WriteLine("下载30%"); Thread.Sleep(1000); Console.WriteLine("下载50%"); Thread.Sleep(1000); Console.WriteLine("下载80%"); Thread.Sleep(1000); Console.WriteLine("下载100%"); } ); Console.WriteLine("结束下载"); } }
3.使用异步async/await完成下载
使用async/await 完成下载逻辑,则代码执行顺序正常,符合业务逻辑: internal class Program { static void Main(string[] args) { DoSomething(); Console.WriteLine("await所在方法外面代码与await异步执行"); Console.ReadLine(); } public static async void DoSomething() { Console.WriteLine("开始下载"); //await所在方法内部的代码与await同步执行 await Task.Run(() => { Thread.Sleep(1000); Console.WriteLine("下载10%"); Thread.Sleep(1000); Console.WriteLine("下载30%"); Thread.Sleep(1000); Console.WriteLine("下载50%"); Thread.Sleep(1000); Console.WriteLine("下载80%"); Thread.Sleep(1000); Console.WriteLine("下载100%"); } ); Console.WriteLine("结束下载"); } }
4.异步IO操作

避免阻塞主线程

//同步读取方法 public string ReadFile(string filePath) { using (FileStream fs = new FileStream(filePath, FileMode.Open)) { using (StreamReader sr = new StreamReader(fs)) { string readStr= sr.ReadToEnd(); return readStr; } } } // 异步读取方法 public async Task<string> ReadFileAsync(string filePath) { using (StreamReader reader = new StreamReader(filePath)) { return await reader.ReadToEndAsync(); } } //异步写入方法 public async Task WriteFileAsync(string filePath, string content) { using (StreamWriter writer = new StreamWriter(filePath)) { await writer.WriteAsync(content); } } public partial class Form3 : Form { public Form3() { InitializeComponent(); //异步写入 Task aaa= IOManage.WriteFileAsync("1.txt","1234"); } private async void button1_Click(object sender, EventArgs e) { //异步读取 Task<string> task1 = IOManage.ReadFileAsync(@"1.txt"); string a = await task1; //显示内容到控件中 button1.Text = a; } }
5.winform使用BeginInvoke (后续讲)
//BeginInvoke serialPort1_DataReceived 本身就是异步方法 不能直接使用 await 选择使用BeginInvoke private void serialPort1_DataReceived(object sender, SerialDataReceivedEventArgs e) { //接收缓冲区数据的字节数 int size = serialPort1.BytesToRead; //动态创建数组接收数据 byte[] buffer = new byte[size]; //读取数据 serialPort1.Read(buffer, 0, buffer.Length); string msg = Encoding.Default.GetString(buffer); //异步执行, //txtReceive.BeginInvoke(msgDelegate, msg); txtReceive.BeginInvoke(new Action<string>(str => { txtReceive.Text = str; }), msg); }
http://www.jsqmd.com/news/898378/

相关文章:

  • 为什么你的ChatGPT面试题总被候选人反向“考倒”?——4大认知偏差陷阱与动态校准公式
  • Outfit字体:9种字重免费开源字体,为你的设计注入品牌灵魂
  • 大型光学红外望远镜拼接镜面主动光学技术【附代码】
  • 保姆级教程:在ArmSoM-W3(RK3588)上配置UART7,让40PIN引脚变身串口调试利器
  • 解锁AI图像新维度:用语言指令实现智能镜头控制
  • 字库芯片驱动与SPI通信实战:在STM32上实现GB18030编码汉字显示
  • Awesome RSS Feeds高级技巧:with_category与without_category文件的区别与应用
  • 【数据校验实战】用 AI 对比源数据库与目标数仓的数据一致性脚本编写
  • Simulink FFT分析:从模型搭建到谐波解读实战指南
  • 探索OpCore Simplify:自动化OpenCore EFI配置的艺术
  • Vue实战(幺捌零):基于 @fullcalendar/vue 打造企业级日程管理系统
  • ARM指令集架构与内存同步指令深度解析
  • 在自动化内容生成场景中利用Taotoken动态选择性价比最优模型
  • ChatGPT法律文件起草实战速成课:7天掌握从Prompt构建→条款溯源→格式合规→电子签章嵌入全流程(含最高院最新电子证据指引适配版)
  • 阻抗匹配介绍
  • Atlas 800I A2 vs Atlas 300I Duo:盘古Pro MoE硬件选型终极指南
  • 2026年第二季度无线投屏软件选型榜,有哪些好用不收费的屏幕镜像软件
  • 写论文如何又快又好?师兄推荐这几个AI论文软件
  • 从Voxblox到Fast Planner:聊聊几种ESDF地图构建方案的性能与选择
  • Atlas OS终极指南:5步打造轻量级高性能Windows系统
  • 基于Rust与AI的命令行纠错工具:从原理到工程实践
  • 3步解锁音乐自由:这款开源工具让你告别格式束缚
  • orange pi 驱动ws2812灯带
  • 电赛备赛避坑:OpenMV巡线代码里那些没人告诉你的ROI框设置细节(附实战配置图)
  • 设计模式(类的拓扑结构)(为什么会产生设计模式,以及什么是设计模式)
  • 如何用AI短视频创作工具3分钟完成专业视频制作:Pixelle-Video完全指南
  • chatgpt参考过往聊天有什么作用?——还可以设置自己的说法风格,如专业型——chat登入用国内手机无法登入,说查找不到手机——可以采用microsoft账号登入,如邮箱登入,点赞不错——也可以点击
  • ZE41镁合金薄壁铸件集成计算与制备工艺【附代码】
  • 神经网络压缩新范式:低熵矩阵表示CER/CSER格式详解与工程实践
  • 全能型 AI写作辅助平台排行榜(2026 优选)