广告位联系
返回顶部
分享到

C#上位机多线程介绍

C#教程 来源:互联网 作者:佚名 发布时间:2026-09-27 21:29:56 人浏览
摘要

C# 多线程的四种打开方式 C# 发展到今天,多线程的写法也经历了几代演变。你不需要全都精通,但至少要知道它们是什么、什么时候用。 方式一:Thread 最原始的一把锤子 Thread是 .NET 最底层的

C# 多线程的四种打开方式

C# 发展到今天,多线程的写法也经历了几代演变。你不需要全都精通,但至少要知道它们是什么、什么时候用。

方式一:Thread — 最原始的一把锤子

Thread 是 .NET 最底层的线程类,给你最大的控制权,也意味着你要自己管好一切。

1

2

3

Thread thread = new Thread(DoWork);

thread.IsBackground = true;  // 设为后台线程,程序退出时自动带走

thread.Start(parameter);

适合什么场景?长时间运行的独立任务,比如一个持续监听硬件数据的采集循环。

缺点也很明显:线程创建和销毁开销大,数量一多系统调度压力就上来了。

方式二:ThreadPool — 线程池,省着点用

既然创建线程贵,那我提前建好一批、用完放回去行不行?这就是线程池的思路。

1

2

3

4

ThreadPool.QueueUserWorkItem(state =>

{

    // 你的后台任务

});

适合短而频繁的小任务。但在上位机开发里用得不算多——因为我们的任务往往是"长驻"的。

方式三:Task — 现代 C# 的标配

Task 是 .NET 4.0 推出的 TPL(任务并行库)的核心,也是目前最主流的写法。

1

2

3

4

5

6

7

8

Task.Run(() =>

{

    // 后台干活

    return result;

}).ContinueWith(t =>

{

    // 干完了通知 UI

}, TaskScheduler.FromCurrentSynchronizationContext());

它比 Thread 轻量,比 ThreadPool 灵活。绝大多数上位机场景,用 Task 就对了。

方式四:async/await — 最优雅的写法

如果说 Task 是标配,那 async/await 就是"顶配"。它让异步代码写起来跟同步代码一样顺。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

private async void btnStart_Click(object sender, EventArgs e)

{

    btnStart.Enabled = false;

    try

    {

        // 后台去读数据,UI 线程不阻塞

        var data = await Task.Run(() => ReadDataFromDevice());

        // await 之后自动回到 UI 线程,直接更新控件

        txtResult.Text = data;

    }

    finally

    {

        btnStart.Enabled = true;

    }

}

注意看,这里没有 Invoke,没有跨线程异常——await 帮你把后续代码自动封送回了 UI 线程。

一句话总结:能写 async/await 就别写别的,代码干净、bug 少。

跨线程更新 UI,这道坎必须过

做上位机,第一个会撞上的墙就是——"线程间操作无效: 从不是创建控件的线程访问它"。

这条规则你得刻在脑子里:只有创建控件的线程,才能修改控件。

WinForms 怎么搞

最朴素的写法,用 InvokeRequired 判断一下:

1

2

3

4

5

6

7

8

9

private void UpdateTextBox(string text)

{

    if (txtLog.InvokeRequired)

    {

        txtLog.Invoke(new Action<string>(UpdateTextBox), text);

        return;

    }

    txtLog.AppendText(text + Environment.NewLine);

}

每个控件都写一遍太麻烦?封装个扩展方法一劳永逸:

1

2

3

4

5

6

7

8

9

10

11

12

public static class ControlExtensions

{

    public static void InvokeIfRequired(this Control c, Action action)

    {

        if (c.InvokeRequired)

            c.Invoke(action);

        else

            action();

    }

}

// 用起来就一行

txtLog.InvokeIfRequired(() => txtLog.AppendText("收到数据啦"));

WPF 怎么搞

WPF 用 Dispatcher,思路是一样的:

1

2

3

4

Application.Current.Dispatcher.Invoke(() =>

{

    txtResult.Text = "数据已更新";

});

如果用了 MVVM 模式,那就更省心了——INotifyPropertyChanged 的绑定机制会自动处理线程切换,你在后台线程改 ViewModel 属性就行。

三个经典场景,拿来就能用

理论说了一堆,不来点实际的怎么行。下面这三个场景,做上位机的大概率会遇到。

场景一:串口数据持续采集

最常见的需求:打开串口,不停读数据,实时显示到界面上。

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

30

31

32

33

private CancellationTokenSource _cts;

private Task _readTask;

private void btnStart_Click(object sender, EventArgs e)

{

    _cts = new CancellationTokenSource();

    _readTask = Task.Run(() => ReadLoop(_cts.Token));

    btnStart.Enabled = false;

    btnStop.Enabled = true;

}

private void ReadLoop(CancellationToken token)

{

    while (!token.IsCancellationRequested)

    {

        try

        {

            string data = serialPort.ReadLine();

            txtLog.InvokeIfRequired(() =>

            {

                txtLog.AppendText($"[{DateTime.Now:HH:mm:ss}] {data}\r\n");

            });

        }

        catch (Exception ex)

        {

            // 异常处理,别让线程崩了

        }

    }

}

private void btnStop_Click(object sender, EventArgs e)

{

    _cts?.Cancel();

    btnStart.Enabled = true;

    btnStop.Enabled = false;

}

注意两个细节:

  • • 用 CancellationToken 来停止,永远不要用 Thread.Abort(),那是暴力手段,容易把资源搞坏
  • • 循环里一定要 try-catch,不然后台线程抛个异常,程序直接就没了

场景二:多设备并行读取

手上有五台设备要轮询,一台一台读太慢?并行走起:

1

2

3

4

5

6

7

8

9

10

private async Task<List<DeviceData>> ReadAllDevicesAsync(){

    var tasks = new List<Task<DeviceData>>();

    foreach (var device in _devices)

    {

        tasks.Add(Task.Run(() => device.ReadData()));

    }

    // 等所有设备都读完

    var results = await Task.WhenAll(tasks);

    return results.ToList();

}

Task.WhenAll 会等所有任务都完成,然后一次性给你结果。配合 async/await,代码干净得不像话。

场景三:生产者-消费者模式

数据采集速度快、处理速度慢,或者 UI 更新太频繁会卡?用队列做个缓冲。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

// 线程安全的队列

private BlockingCollection<DataFrame> _dataQueue = new BlockingCollection<DataFrame>();

// 生产者:采集线程,只管往队列里塞

void ProducerLoop()

{

    while (true)

    {

        var frame = ReadFrameFromHardware();

        _dataQueue.Add(frame);

    }

}

// 消费者:处理线程,慢慢从队列里取

void ConsumerLoop()

{

    foreach (var frame in _dataQueue.GetConsumingEnumerable())

    {

        ProcessAndDisplay(frame);

    }

}

BlockingCollection 是个好东西——队列空的时候消费者自动阻塞,不占 CPU;有数据来了自动唤醒。做数据缓冲、日志写入都很好用。


版权声明 : 本文内容来源于互联网或用户自行发布贡献,该文观点仅代表原作者本人。本站仅提供信息存储空间服务和不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权, 违法违规的内容, 请发送邮件至2530232025#qq.cn(#换@)举报,一经查实,本站将立刻删除。
原文链接 :
相关文章
  • C#上位机多线程介绍
    C# 多线程的四种打开方式 C# 发展到今天,多线程的写法也经历了几代演变。你不需要全都精通,但至少要知道它们是什么、什么时候用。
  • C#三步实现Word转Excel并保留格式

    C#三步实现Word转Excel并保留格式
    在许多业务流程中,重要数据通常以 Word 文档的形式存在,例如报告、发票、客户清单或其他结构化列表。Microsoft Word 在排版、格式化和展
  • C#代码实现生成带Logo的二维码
    在生成二维码时,您可能希望在其中加入自定义图片,例如公司的 Logo 或个人头像。本文将介绍如何使用Spire.Barcode for .NET库,通过C# 和 VB
  • C#使用Spire.PDF实现PDF与OFD的互转
    在日常办公和电子文档处理中,PDF和OFD是两种最常见的电子文档格式。PDF作为国际通用格式广为人知,而OFD作为国家标准版式文档格式,在
  • 使用C#创建PDF文档的完整教程(文本、图片、形状

    使用C#创建PDF文档的完整教程(文本、图片、形状
    在企业级应用和信息系统中,PDF 一直是最常见、也是最稳定的文档输出格式之一。无论是财务报表、合同文档、业务统计报告,还是系统自
  • C# Spire.XLS for .NET轻松实现Excel到TXT的转换
    在数据处理领域,Excel文件以其强大的功能占据着举足轻重的地位。然而,在诸多应用场景下,我们却需要将这些结构化的Excel数据转换为更
  • C#中弱引用使用介绍

    C#中弱引用使用介绍
    弱引用(Weak Reference)是一种特殊的引用类型,它允许你引用一个对象,但不会阻止该对象被垃圾回收器(GC)回收。弱引用通常用于需要缓
  • C++ move的作用介绍及陷阱最佳实践
    C++ move 的作用详解 这是个核心概念题!让我从浅到深给你讲清楚。 一、一句话总结 std::move的作用:将对象转换为右值引用,允许资源被移
  • C#借助Spire.XLS for .NET实现在Excel中添加文档属性
    在日常的数据处理和项目管理中,Excel文档扮演着举足轻重的角色。然而,除了单元格中的数据,你是否曾关注过Excel文件的幕后信息文档属
  • Android实现监听手机开机事件的多种方法
    在开发Android应用时,有时我们需要在设备启动完成后执行某些操作,比如检查更新、同步数据等。为了实现这一功能,我们需要监听 设备的
  • 本站所有内容来源于互联网或用户自行发布,本站仅提供信息存储空间服务,不拥有版权,不承担法律责任。如有侵犯您的权益,请您联系站长处理!
  • Copyright © 2017-2022 F11.CN All Rights Reserved. F11站长开发者网 版权所有 | 苏ICP备2022031554号-1 | 51LA统计