百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 热门文章 > 正文

C#的多线程技术及应用

bigegpt 2024-09-17 12:23 7 浏览

一、基本概念

C#(C Sharp)线程技术用于实现多线程编程,以便在程序中同时执行多个任务。C#提供了丰富的线程库和相关功能,可以轻松地管理多线程应用程序。涉及C#线程技术的相关内容:

  1. Thread 类: C#的System.Threading命名空间中提供了Thread类,用于创建和管理线程。通过Thread类,您可以创建新的线程,设置线程的优先级、状态等属性,以及控制线程的启动和终止。
  2. ThreadStart 委托: 使用ThreadStart委托来指定线程应该执行的方法。这个方法将在新线程中执行。
  3. Lambda 表达式: 您可以使用Lambda表达式来定义线程执行的代码块,这样可以更简洁地指定线程的操作。
  4. 线程同步: 多线程应用程序可能涉及到共享资源的并发访问,这可能导致数据竞争和错误。C#提供了锁、互斥体、信号量等线程同步机制,以确保线程安全性。
  5. Task 和 Task Parallel Library(TPL): .NET Framework引入了Task类和TPL,使得编写异步和并行代码更加容易。Task可以表示一个异步操作,TPL提供了更高级的并行编程模型。
  6. async/await 关键字: C# 5.0 引入了async和await关键字,使得异步编程变得更加直观。您可以使用它们来编写非阻塞的异步代码。
  7. 线程池: C#中的线程池允许您从线程池中获取可用的线程,从而避免频繁地创建和销毁线程,提高效率。
  8. 取消线程: C#允许您在执行中的线程中安全地取消操作,以便控制线程的执行。
  9. 线程安全集合: C#提供了一系列线程安全的集合,如ConcurrentQueue、ConcurrentDictionary等,用于在多线程环境中安全地操作数据。
  10. 线程异常处理: 您可以捕获并处理线程中的异常,以保证程序的稳定性。

在使用C#进行多线程编程时,需要仔细考虑线程安全性、资源竞争等问题,并选择适当的线程技术来满足应用程序的需求。

二、Task类的使用

Task类是C#中用于处理并发编程和异步操作的一个重要工具,使得管理并行任务和异步操作更加简便。

  • 创建任务: 使用Task类的构造函数或Task.Factory.StartNew方法创建任务。您可以使用构造函数提供一个委托,也可以使用StartNew方法提供一个Action或Func委托。
    1. 等待任务完成: 使用Task.Wait或Task.WaitAll方法来等待一个或多个任务完成。这会阻塞当前线程,直到任务完成。
    2. 异步操作: 使用Task.Run或Task.Factory.StartNew方法来执行异步操作。在异步方法中,可以使用async/await关键字来使代码更具可读性。
    3. 任务连续: 使用ContinueWith方法来定义在任务完成后要执行的操作,或使用await关键字在异步方法中实现任务连续。
    4. 任务取消: 使用CancellationToken来取消任务。可以在任务中轮询CancellationToken.IsCancellationRequested属性,或使用CancellationToken.ThrowIfCancellationRequested()来检查取消请求。
    5. 任务异常处理: 使用Task.Exception属性来检查任务是否抛出了异常,或使用Try-Catch块来处理异常。
    6. 任务状态: 使用Task.Status属性来获取任务的当前状态,如Running、WaitingToRun、Canceled等。

    以下是一个使用Task类执行异步操作的示例,计算两个数字的和:

    using System;
    using System.Threading.Tasks;
    
    class Program
    {
        static async Task Main(string[] args)
        {
            // 使用async/await创建异步方法
            int result = await CalculateSumAsync(5, 10);
            
            Console.WriteLine(#34;计算结果:{result}");
        }
    
        static async Task<int> CalculateSumAsync(int a, int b)
        {
            // 使用Task.Run创建异步操作
            int sum = await Task.Run(() =>
            {
                int result = a + b;
                return result;
            });
    
            return sum;
        }
    }

    示例中,创建了一个异步方法CalculateSumAsync,其中使用了Task.Run来执行计算操作。在Main方法中,使用await关键字等待异步方法完成,并输出计算结果。

    三、几个多场景案例

    使用Task类的更多示例,涵盖了不同的场景和用法:

    示例1:并行执行任务

    使用Parallel.Invoke同时启动多个任务。

    using System;
    using System.Threading.Tasks;
    
    class Program
    {
        static void Main(string[] args)
        {
            Parallel.Invoke(
                () => DoWork("Task 1"),
                () => DoWork("Task 2"),
                () => DoWork("Task 3")
            );
        }
    
        static void DoWork(string taskName)
        {
            Console.WriteLine(#34;Starting {taskName}");
            Task.Delay(1000).Wait(); // 模拟耗时操作
            Console.WriteLine(#34;{taskName} completed");
        }
    }

    示例2:连续执行任务

    任务1执行完后,任务2会连续执行。

    using System;
    using System.Threading.Tasks;
    
    class Program
    {
        static async Task Main(string[] args)
        {
            await Task.Run(() => Console.WriteLine("Task 1"))
                      .ContinueWith(task => Console.WriteLine("Task 2"));
        }
    }

    示例3:任务取消

    使用CancellationToken来取消任务。

    using System;
    using System.Threading;
    using System.Threading.Tasks;
    
    class Program
    {
        static async Task Main(string[] args)
        {
            CancellationTokenSource cts = new CancellationTokenSource();
    
            Task longRunningTask = Task.Run(() =>
            {
                Console.WriteLine("Long running task started...");
                Thread.Sleep(3000);
                Console.WriteLine("Long running task completed...");
            }, cts.Token);
    
            await Task.Delay(1000); // 假设在1秒后需要取消任务
            cts.Cancel();
    
            try
            {
                await longRunningTask;
            }
            catch (TaskCanceledException)
            {
                Console.WriteLine("Task was canceled.");
            }
        }
    }


    四、使用异步程序向多个控件添加数据

    使用 C# 中的异步编程,启动多个函数,将它们的输出数据分别添加到不同的控件(如 ListView、TreeView、RichTextBox)中。

    using System;
    using System.Diagnostics;
    using System.Threading.Tasks;
    using System.Windows.Forms;
    
    namespace ParallelExecutionExample
    {
        public partial class MainForm : Form
        {
            public MainForm()
            {
                InitializeComponent();
            }
    
            private async void MainForm_Load(object sender, EventArgs e)
            {
                await RunFunctionsAndPopulateControlsAsync();
            }
    
            private async Task RunFunctionsAndPopulateControlsAsync()
            {
                Task[] tasks = new Task[]
                {
                    RunFunctionAsync(AddToListView),
                    RunFunctionAsync(AddToTreeView),
                    RunFunctionAsync(AddToRichTextBox)
                };
    
                await Task.WhenAll(tasks);
            }
    
            private async Task RunFunctionAsync(Action<string> action)
            {
                await Task.Run(() =>
                {
                    string output = GetFunctionOutput();
                    action(output);
                });
            }
    
            private void AddToListView(string output)
            {
                if (listView1.InvokeRequired)
                {
                    listView1.Invoke(new Action(() =>
                    {
                        ListViewItem item = new ListViewItem(output);
                        listView1.Items.Add(item);
                    }));
                }
                else
                {
                    ListViewItem item = new ListViewItem(output);
                    listView1.Items.Add(item);
                }
            }
    
            private void AddToTreeView(string output)
            {
                if (treeView1.InvokeRequired)
                {
                    treeView1.Invoke(new Action(() =>
                    {
                        TreeNode node = new TreeNode(output);
                        treeView1.Nodes.Add(node);
                    }));
                }
                else
                {
                    TreeNode node = new TreeNode(output);
                    treeView1.Nodes.Add(node);
                }
            }
    
            private void AddToRichTextBox(string output)
            {
                if (richTextBox1.InvokeRequired)
                {
                    richTextBox1.Invoke(new Action(() =>
                    {
                        richTextBox1.AppendText(output + Environment.NewLine);
                    }));
                }
                else
                {
                    richTextBox1.AppendText(output + Environment.NewLine);
                }
            }
    
            private string GetFunctionOutput()
            {
                // 模拟函数的输出
                return "Function output at " + DateTime.Now.ToString("HH:mm:ss");
            }
        }
    }

    窗体加载后,使用 RunFunctionsAndPopulateControlsAsync 方法并行启动多个函数,并将它们的输出分别添加到不同的控件中。

    RunFunctionAsync 方法在新线程中运行函数,然后根据不同的控件类型,调用相应的添加函数。每个添加函数在添加数据时都会判断是否需要通过 Invoke 调用来更新 UI 线程。

    相关推荐

    Go语言泛型-泛型约束与实践(go1.7泛型)

    来源:械说在Go语言中,Go泛型-泛型约束与实践部分主要探讨如何定义和使用泛型约束(Constraints),以及如何在实际开发中利用泛型进行更灵活的编程。以下是详细内容:一、什么是泛型约束?**泛型...

    golang总结(golang实战教程)

    基础部分Go语言有哪些优势?1简单易学:语法简洁,减少了代码的冗余。高效并发:内置强大的goroutine和channel,使并发编程更加高效且易于管理。内存管理:拥有自动垃圾回收机制,减少内...

    Go 官宣:新版 Protobuf API(go pro版本)

    原文作者:JoeTsai,DamienNeil和HerbieOng原文链接:https://blog.golang.org/a-new-go-api-for-protocol-buffer...

    Golang开发的一些注意事项(一)(golang入门项目)

    1.channel关闭后读的问题当channel关闭之后再去读取它,虽然不会引发panic,但会直接得到零值,而且ok的值为false。packagemainimport"...

    golang 托盘菜单应用及打开系统默认浏览器

    之前看到一个应用,用go语言编写,说是某某程序的windows图形化客户端,体验一下发现只是一个托盘,然后托盘菜单的控制面板功能直接打开本地浏览器访问程序启动的webserver网页完成gui相关功...

    golang标准库每日一库之 io/ioutil

    一、核心函数概览函数作用描述替代方案(Go1.16+)ioutil.ReadFile(filename)一次性读取整个文件内容(返回[]byte)os.ReadFileioutil.WriteFi...

    文件类型更改器——GoLang 中的 CLI 工具

    我是如何为一项琐碎的工作任务创建一个简单的工具的,你也可以上周我开始玩GoLang,它是一种由Google制作的类C编译语言,非常轻量和快速,事实上它经常在Techempower的基准测...

    Go (Golang) 中的 Channels 简介(golang channel长度和容量)

    这篇文章重点介绍Channels(通道)在Go中的工作方式,以及如何在代码中使用它们。在Go中,Channels是一种编程结构,它允许我们在代码的不同部分之间移动数据,通常来自不同的goro...

    Golang引入泛型:Go将Interface「」替换为“Any”

    现在Go将拥有泛型:Go将Interface{}替换为“Any”,这是一个类型别名:typeany=interface{}这会引入了泛型作好准备,实际上,带有泛型的Go1.18Beta...

    一文带你看懂Golang最新特性(golang2.0特性)

    作者:腾讯PCG代码委员会经过十余年的迭代,Go语言逐渐成为云计算时代主流的编程语言。下到云计算基础设施,上到微服务,越来越多的流行产品使用Go语言编写。可见其影响力已经非常强大。一、Go语言发展历史...

    Go 每日一库之 java 转 go 遇到 Apollo?让 agollo 来平滑迁移

    以下文章来源于GoOfficialBlog,作者GoOfficialBlogIntroductionagollo是Apollo的Golang客户端Apollo(阿波罗)是携程框架部门研...

    Golang使用grpc详解(golang gcc)

    gRPC是Google开源的一种高性能、跨语言的远程过程调用(RPC)框架,它使用ProtocolBuffers作为序列化工具,支持多种编程语言,如C++,Java,Python,Go等。gR...

    Etcd服务注册与发现封装实现--golang

    服务注册register.gopackageregisterimport("fmt""time"etcd3"github.com/cor...

    Golang:将日志以Json格式输出到Kafka

    在上一篇文章中我实现了一个支持Debug、Info、Error等多个级别的日志库,并将日志写到了磁盘文件中,代码比较简单,适合练手。有兴趣的可以通过这个链接前往:https://github.com/...

    如何从 PHP 过渡到 Golang?(php转golang)

    我是PHP开发者,转Go两个月了吧,记录一下使用Golang怎么一步步开发新项目。本着有坑填坑,有错改错的宗旨,从零开始,开始学习。因为我司没有专门的Golang大牛,所以我也只能一步步自己去...