任务、后台工作线程和线程——并发的简单比较

文章探讨了C#中三种并发执行方式:线程、BackgroundWorker和Task。Task作为现代.NET应用的首选,提供了内置的取消、进度报告和异常处理支持,以及与async/await模式的兼容性。线程由操作系统管理,低开销但使用复杂,而BackgroundWorker适合GUI应用,提供取消和进度更新。三种方式各有优缺点,Task在现代编程中占据主导地位。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

方法

线程

Background Workers

任务

摘要——任务FTW?


即使你不熟悉C#,也可能至少遇到过TaskThreadBackgroundWorker中的一个。如果再花一点时间,您可能已经在旅途中看到了这三者。它们都是在C#中运行并发代码的方法,每种方法都有自己的优点和缺点。在本文中,我们将探讨每个如何在高层次上运作。值得注意的是,在大多数现代.NET应用程序和库中,你会看到内容会聚合到Task

方法

我继续创建了一个测试应用程序,您可以在此处找到该应用程序。因为这是在源代码管理中,所以它可能/很可能与我们在本文中看到的内容有所不同,所以我只是想为作为读者的您提供免责声明。

该应用程序允许我们选择要运行的不同示例。我将首先粘贴下面的代码,以便您可以看到工作原理。

using System.Globalization;

internal sealed class Program
{
    private static readonly IReadOnlyDictionary<int, IExample> _examples =
        new Dictionary<int, IExample>()
        {
            [1] = new NonBackgroundThreadExample(),
            [2] = new BackgroundThreadExample(),
            [3] = new BackgroundWorkerExample(),
            [4] = new SimultaneousExample(),
        };

    private static void Main(string[] args)
    {
        Console.WriteLine("Enter the number for one of the following examples to run:");
        foreach (var entry in _examples)
        {
            Console.WriteLine("----");
            var restoreColor = Console.ForegroundColor;
            Console.ForegroundColor = ConsoleColor.Cyan;
            Console.WriteLine($"Choice: {entry.Key}");
            Console.ForegroundColor = ConsoleColor.Magenta;
            Console.WriteLine($"Name: {entry.Value.Name}");
            Console.ForegroundColor = ConsoleColor.Yellow;
            Console.WriteLine($"Description: {entry.Value.Description}");
            Console.ForegroundColor = restoreColor;
        }

        Console.WriteLine("----");

        IExample example;
        while (true)
        {
            var input = Console.ReadLine();
            if (string.IsNullOrWhiteSpace(input))
            {
                Console.WriteLine("Would you like to exit? Y/N");
                input = Console.ReadLine();
                if ("y".Equals(input, StringComparison.OrdinalIgnoreCase))
                {
                    return;
                }

                Console.WriteLine("Please make another selection.");
                continue;
            }

            if (!int.TryParse(input, NumberStyles.Integer, 
                 CultureInfo.InvariantCulture, out var exampleId) ||
                !_examples.TryGetValue(exampleId, out example))
            {
                Console.WriteLine("Invalid input. Please make another selection.");
                continue;
            }

            break;
        }

        Console.WriteLine($"Starting example '{example.Name}'...");
        Console.WriteLine("-- Before entering example method");
        example.ExecuteExample();
        Console.WriteLine("-- After leaving example method");
    }
}

线程

线程是C#中最基本的并发执行形式。它们由操作系统创建和管理,可用于与执行主线程并行运行代码。线程的概念是一般编程的并发性时最基本的构建块之一。但是,它也是一个类的名称,我们可以在C#中直接使用它来运行并发代码。

线程允许您传入要执行的方法。它们也可以标记为后台或不是后台,当应用程序尝试退出时,后台线程将被终止。相反,非后台线程将尝试使应用程序保持活动状态,直到线程退出。

下面是创建和启动新线程的示例:

Thread newThread = new Thread(new ThreadStart(MyMethod));
newThread.Start();

使用Thread的一个主要优点是它们的开销很低,因为它们由操作系统直接管理。但是,它们可能比其他并发选项更难使用,因为它们没有对取消、进度报告或异常处理的内置支持。在C#中,我们已经可以访问该Thread对象很长时间了,因此在此基础上构建其他构造是有意义的,以便我们添加额外的生活质量增强功能。

让我们看看第一个线程示例

public void ExecuteExample()
{
    void DoWork(string label)
    {
        while (true)
        {
            Task.Delay(1000).Wait();
            Console.WriteLine($"Waiting in '{label}'...");
        }
    };

    var thread = new Thread(new ThreadStart(() => DoWork("thread")));
    thread.Start();

    Console.WriteLine("Press enter to exit!");
    Console.ReadLine();
}

在我们的示例应用程序的上下文中,我们将能够在Thread运行时看到方法打印到控制台。但是,当用户按Enter时,示例方法将退出,然后程序也将尝试退出。因为Thread没有标记为后台,所以实际上会阻止应用程序自然终止!试试看。

我们可以直接将其与第二个示例进行比较,第二个示例有一个区别:Thread标记为后台运行。当您尝试此示例时,您会注意到正在运行的线程不会阻止应用程序退出!

Background Workers

BackgroundWorkerC#中更高级别的并发执行选项。它是包含在System.ComponentModel命名空间中的组件,通常您可以在GUI应用程序中看到它。例如,经典的WinForms应用程序将利用这些。

让我们看一下 Background Worker的示例

 public void ExecuteExample()
    {
        void DoWork(string label)
        {
            while (true)
            {
                Task.Delay(1000).Wait();
                Console.WriteLine($"Waiting in '{label}'...");
            }
        };

        var backgroundWorker = new BackgroundWorker();
        // NOTE: RunWorkerCompleted may not have a chance to run 
        // before the application exits
        backgroundWorker.RunWorkerCompleted += 
        (s, e) => Console.WriteLine("Background worker finished.");
        backgroundWorker.DoWork += (s, e) => DoWork("background worker");
        backgroundWorker.RunWorkerAsync();

        Console.WriteLine("Press enter to exit!");
        Console.ReadLine();
    }

使用BackgroundWorker的一个主要优点是它具有对取消、进度报告和异常处理的内置支持。它的设置略有不同,因为事件处理程序已注册到BackgroundWorker。您还可以将完成处理程序和其他处理程序注册到对象。就像标记为后台运行的Thread一样,BackgroundWorker不会阻止应用程序退出。

任务

任务是C# 4.0中作为任务并行库(TPL)的一部分引入的新式并发执行选项。它们类似于线程,但由.NET运行时而不是操作系统管理。下面是一个示例,说明了在后台运行Task是多么简单:

Task.Run(() => SomeMethod());

使用Task的主要优点是它们内置了对取消、进度报告和异常处理的支持,类似于BackgroundWorker。此外,它们比Thread更易于使用,因为它们由.NET运行时管理。Task还支持 async/await 模式,该模式允许您编写外观和行为类似于同步代码的异步代码。

在我们的最后一个示例中,我们可以看到Task正在设置并沿着ThreadBackgroundWorker运行。这里没有async/await修饰,因为我想尽可能直接地进行比较。

public void ExecuteExample()
    {
        var cancellationTokenSource = new CancellationTokenSource();
        void DoWork(string label)
        {
            while (!cancellationTokenSource.IsCancellationRequested)
            {
                Task.Delay(1000).Wait();
                Console.WriteLine($"Waiting in '{label}'...");
            }
        };

        var thread = new Thread(new ThreadStart(() => DoWork("thread")));
        thread.Start();

        var backgroundWorker = new BackgroundWorker();
        // NOTE: RunWorkerCompleted may not have a chance to run 
        // before the application exits
        backgroundWorker.RunWorkerCompleted += 
        (s, e) => Console.WriteLine("Background worker finished.");
        backgroundWorker.DoWork += (s, e) => DoWork("background worker");
        backgroundWorker.RunWorkerAsync();

        var task = Task.Run(() => DoWork("task"));

        Console.WriteLine("Press enter to exit!");
        Console.ReadLine();
        cancellationTokenSource.Cancel();
    }

在上面的代码中,我们可以看到CancellationToken的使用。这允许我们在循环尝试下一次迭代时中断循环的执行。但是,Task很像BackgroundWorker和标记为后台运行的Thread,因为启动Task不会阻止应用程序退出。

摘要——任务FTW

多年来,在C#中提供并发的选项一直在发展。Task似乎是现在实现并发模式的首选。但是,如果您需要最大的性能和控制,Thread可能是另一种选择。如果您正在寻找运行一些工作并对进度报告提供简单的支持,BackgroundWorker则可能是一个有用的选择。在这些选项中,如果您需要一个支持async/await模式的现代且易于使用的选项,Task可能是最佳选择。

所有这些都是您可以使用的工具,希望您对它们各自的功能有更多的了解。

本文最初发表于 Tasks, BackgroundWorkers, and Threads - Simple Comparisons

https://www.codeproject.com/Articles/5352757/Tasks-BackgroundWorkers-and-Threads-Simple-Compari

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值