尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
C# 工控上位机系列(五):多线程入门!Thread 基础使用与线程安全实战
写在前面第四篇开的线程这篇给你讲透各位小伙伴上一篇我们讲了跨线程更新 UI里面随手就new Thread()开了个线程。但很多同学反馈线程到底怎么创建前台后台有啥区别多个线程同时改一个变量为什么结果不对今天这篇就把这些问题一次性讲清楚。从Thread 基础用法、线程生命周期到最核心的线程安全与 lock 锁全部配上可运行的代码和工控实战场景学完就能在项目里正确使用多线程。一、为什么上位机开发必须用多线程先看一个最常见的场景点击按钮后去读 PLC 数据读取需要 3 秒。private void btnRead_Click(object sender, EventArgs e) { lblStatus.Text 读取中...; var data ReadFromPlc(); // 耗时3秒UI线程被阻塞 lblStatus.Text 读取完成 data; }运行后你会发现界面直接卡死 3 秒按钮点不动窗口拖不动。因为 WinForms 的 UI 线程既要响应界面操作又要执行读取逻辑两者互斥。解决办法就是把耗时操作放到另一个线程里跑UI 线程只负责界面响应private void btnRead_Click(object sender, EventArgs e) { lblStatus.Text 读取中...; // 开新线程执行耗时操作 Thread t new Thread(() { var data ReadFromPlc(); // 跨线程更新UI上一篇学的Invoke this.Invoke(new Action(() lblStatus.Text 读取完成 data)); }); t.IsBackground true; t.Start(); }二、Thread 基础用法手把手2.1 创建与启动线程// 方式一传入方法名 Thread t1 new Thread(DoWork); t1.Start(); // 方式二Lambda 表达式最常用简洁 Thread t2 new Thread(() { Console.WriteLine(线程执行中); }); t2.Start(); void DoWork() { Console.WriteLine(线程执行中); }2.2 给线程传参数// 方式一ParameterizedThreadStart参数必须是object Thread t1 new Thread((obj) { int id (int)obj; Console.WriteLine($线程编号{id}); }); t1.Start(100); // 方式二Lambda 闭包推荐类型安全可传多个参数 int axisId 1; string axisName X轴; Thread t2 new Thread(() { Console.WriteLine($轴编号{axisId}轴名称{axisName}); }); t2.Start();2.3 前台线程 vs 后台线程类型默认值程序退出时的行为前台线程IsBackground false默认程序会等它执行完才退出后台线程IsBackground true程序退出时直接终止不等它上位机建议采集、通信这类工作线程一律设为后台线程IsBackground true否则关程序时可能因为前台线程没结束而进程残留。2.4 线程休眠与等待// Sleep当前线程暂停指定毫秒 Thread.Sleep(1000); // 暂停1秒 // Join等待另一个线程执行完再继续 Thread worker new Thread(DoWork); worker.Start(); worker.Join(); // 主线程在这里等worker跑完 Console.WriteLine(worker已完成);三、线程生命周期与状态一个线程从创建到结束会经历以下状态状态说明Unstarted已创建但未调用 Start()Running正在执行WaitSleepJoin调用了 Sleep / Join / 等待锁处于阻塞Stopped执行完毕正常结束Aborted被强制终止不推荐使用 Abort可以通过thread.ThreadState查看当前状态。实际开发中不用太纠结状态名记住Start 启动、Sleep 暂停、Join 等待、跑完自动结束就够了。四、线程安全多个线程同时改一个变量会怎样这是多线程最核心的问题。先看一段代码int count 0; void Increment() { for (int i 0; i 10000; i) { count; // 看似简单实际分三步读→加1→写回 } } // 开10个线程同时累加 Thread[] threads new Thread[10]; for (int i 0; i 10; i) { threads[i] new Thread(Increment); threads[i].Start(); } foreach (var t in threads) t.Join(); Console.WriteLine($期望结果100000实际结果{count}); // 实际结果几乎一定小于100000而且每次都不一样为什么结果不对因为count不是原子操作它实际分三步从内存读取 count 的值CPU 把值加 1把结果写回内存当两个线程同时执行时可能发生线程A读到 5线程B也读到 5各自加1后都写回 6——两次累加只生效了一次。这就是竞态条件Race Condition。五、lock 锁最基础的线程安全方案解决办法就是用lock把共享资源的访问包起来保证同一时刻只有一个线程能进入int count 0; // 锁对象private、readonly、引用类型不要用值类型 private readonly object _lockObj new object(); void Increment() { for (int i 0; i 10000; i) { lock (_lockObj) { count; // 同一时刻只有一个线程能执行这里 } } } // 这次结果一定是100000lock 的三个关键点锁对象要 private readonly不要用lock(this)或lock(typeof(类名))外部代码也可能锁同一个对象导致死锁。锁的粒度要合适锁太大影响性能锁太小保护不住。只锁必须保护的共享资源访问。锁内不要做耗时操作比如 lock 里调用 Sleep、网络请求会导致其他线程长时间等待。底层预告这里我们只讲 lock 的基础用法。lock 编译后到底变成了什么、为什么必须锁引用类型、死锁怎么产生和排查这些进阶内容我会在后面《线程同步深入》一篇里详细拆解先把基础用法练熟。六、工控实战多线程采集 安全写日志结合上位机场景两个轴同时采集数据都要写同一份日志文件用 lock 保证不冲突。public class AxisCollector { private readonly object _logLock new object(); private readonly StreamWriter _logWriter; public AxisCollector(string logPath) { _logWriter new StreamWriter(logPath, true); } // 采集线程入口 public void StartCollect(string axisName, int intervalMs) { Thread t new Thread(() { while (true) { double pos ReadAxisPosition(axisName); WriteLog($[{DateTime.Now:HH:mm:ss.fff}] {axisName} 位置{pos:F3}); Thread.Sleep(intervalMs); } }); t.IsBackground true; t.Start(); } // 线程安全的日志写入 private void WriteLog(string message) { lock (_logLock) { _logWriter.WriteLine(message); _logWriter.Flush(); } } private double ReadAxisPosition(string axisName) { // 模拟从运动控制卡读取位置 return new Random().NextDouble() * 100; } } // 使用同时启动X轴和Y轴采集 var collector new AxisCollector(axis_log.txt); collector.StartCollect(X轴, 100); collector.StartCollect(Y轴, 100);两个线程同时写日志如果没有 lock可能出现两行日志交错、文件损坏。加了 lock 后每次只有一个线程能写日志完整有序。七、新手高频易错点建议收藏闭包陷阱循环变量捕获for循环里开线程用循环变量所有线程可能拿到同一个最终值。解决在循环内用局部变量拷贝// 错误所有线程可能都打印5 for (int i 0; i 5; i) { new Thread(() Console.WriteLine(i)).Start(); } // 正确每次循环拷贝一份 for (int i 0; i 5; i) { int temp i; new Thread(() Console.WriteLine(temp)).Start(); }前台线程导致程序关不掉忘记设IsBackground true关闭窗口后进程还在任务管理器里残留。工作线程一律设后台。lock(this) 的隐患外部代码也可能 lock 你的实例对象造成意外死锁。始终用 private readonly 的锁对象。锁内做耗时操作lock 里 Sleep 或发网络请求其他线程全部阻塞性能暴跌。用 Abort 强制终止线程Thread.Abort()可能在任意时刻终止导致资源未释放、状态不一致。推荐用CancellationToken优雅停止后面异步篇会讲。八、本篇小结多线程让耗时操作不阻塞 UI是上位机开发的基本功。Thread 创建 Start 启动Lambda 闭包传参最方便。工作线程设IsBackground true避免程序退出后进程残留。多个线程同时改共享变量会产生竞态条件结果不可预期。lock是最基础的线程安全方案锁对象要 private readonly锁内不做耗时操作。闭包陷阱、前台线程残留、lock(this) 是新手三大坑。补充说明本篇讲的 Thread 是最底层的线程操作。.NET 还提供了更现代的Task async/await异步编程模型基于线程池、自动管理线程生命周期写起来更简洁。这个我们放到后面异步编程篇详细讲这里先把 Thread 底层吃透后面学 Task 才会真正理解它帮你省了什么。下一篇预告C# Timer 三大类对比上位机轮询到底该用哪个我会把 WinForms.Timer、System.Timers.Timer、Threading.Timer 的区别和适用场景一次讲透为后面的生产者-消费者模型打基础。
RELATED

相关推荐

电柜空间里的能量战争:09 接地母排:整个柜子的能量高速公路

电柜空间里的能量战争:09 接地母排:整个柜子的能量高速公路

第九篇 接地母排:整个柜子的能量高速公路 —— 为什么一个控制柜最重要的铜,不是电源铜排,而是接地铜排? 在能量秩序的漫长修行之路上,你我已从最初的“换件工”,逐步蜕变为能够洞察空间边界、近场风暴与能量路径的“能量秩序守护者”。 我们看过了风扇孔的天线本质、…

📅 2026/11/6 1:26:12
FFmpeg6麦克风pcm录音实战——mp3/aac/m4a格式都是支持的

FFmpeg6麦克风pcm录音实战——mp3/aac/m4a格式都是支持的

可以这么说,用FFmpeg的API搞定麦克风的mp3/aac录音,相当于是音视频开发中前进的不大不小的一步,录音的整个过程其实和推流几乎相同,包含取流、封装(复用)、编码、收尾等工作,搞明白了录音,推流就比较简单了,推流的关键是设置好复用器参数和做好音视频同步。录音程序中…

📅 2026/11/7 2:24:32
安卓听写神器AI语音朗读支持拍照识别课本

安卓听写神器AI语音朗读支持拍照识别课本

语音朗读 家里有娃的要听好了。辅导孩子听写单词这事儿,多少家长被折磨过?你念一个他写一个,念完了还得检查对不对,来来回回几轮下来嗓子都哑了。关键是你的发音准不准都两说,万一念错了,孩子写对了反而被…

📅 2026/10/17 16:19:12
MORE NEWS

更多资讯

📰

睢县 低压电工证实操考试考什么

睢县低压电工证上岗必备,学历不高也能考吗? 你是不是也担心自己学历不够,根本报不上睢县低压电工证?别慌,这证其实和学历关系不大,只要你满足基本条件,照样能考。别再被“文凭门槛”绊住脚步,低压电工证是很多岗位的 上岗必备…

📰

睢县 低压电工证模拟考试题库

睢县低压电工证怎么考?官方回应帮你避开报名坑 你是不是也正愁不知道睢县低压电工证怎么报名?怕被那些中介套路,花钱还拿不到证?别急,今天我就从一个干了10年应急管理行业的老手角度,带你一步步搞清楚怎么在睢县考低压电工证,避免踩坑!…

📰

涵江考电工证多少钱?怕考不过白交培训费?这些细节必须知道

涵江考电工证多少钱?怕考不过白交培训费?这些细节必须知道 怕考不过白交培训费?这是很多想考电工证的朋友最担心的事。特别是现在市面上培训机构五花八门,价格参差不齐,不少人担心花了钱最后还是拿不到证。今天咱们就来聊聊 涵江考电工证…

📰

西安低压电工证考证含金量高吗?怕考不过白交培训费的看过来

西安低压电工证考证含金量高吗?怕考不过白交培训费的看过来 怕考不过白交培训费?别急,西安低压电工证考证含金量高不高,得看你是怎么准备的。很多人以为只要找培训机构报个名就能拿证,结果一考试就挂,钱花了,时间也浪费了。其实,低压电工证考试并不是…

📰

英州哪里可以考电工证官方回应证书过期了不知道怎么复审延期

英州哪里可以考电工证官方回应证书过期了不知道怎么复审延期 证书过期了不知道怎么复审延期,这是很多电工从业者最头疼的问题之一,尤其是英州地区的朋友,常常因为不了解流程和官方政策,导致证书失效影响工作。今天我们就来聊聊【英州哪里可以考电工证】的…

📰

电工证证件从哪里查询 一位焦作学员的真实经历告诉你怎么避坑

电工证证件从哪里查询 一位焦作学员的真实经历告诉你怎么避坑 不知道去哪报名怕被中介坑?这是很多想考电工证的人最真实的感受。尤其是像我这样从零开始准备考试的人,一开始对流程、机构、考试内容都一无所知,结果差点被一些不靠谱的培训机构骗了报名费,…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬