19、多线程与异步编程对比

多线程与异步编程对比

知识点

基本概念

  • 多线程编程:创建和管理多个执行线程来并行处理任务
  • 异步编程:通过非阻塞方式处理耗时操作,提高应用程序响应性
  • 并发 vs 并行:并发是同时处理多个任务,并行是同时执行多个任务

主要区别

  1. 资源消耗:线程消耗更多系统资源,Task 利用线程池更高效
  2. 编程复杂度:Task 提供更高级的抽象,编程更简单
  3. 错误处理:Task 有更好的异常传播机制
  4. 组合性:Task 支持更强大的组合操作

代码样例

传统多线程示例

using System;
using System.Threading;

public class MultiThreadingExample
{
    public static void TraditionalThreadingDemo()
    {
        Console.WriteLine("=== 传统多线程演示 ===");
        Console.WriteLine($"主线程ID: {Thread.CurrentThread.ManagedThreadId}");

        // 创建多个线程
        Thread[] threads = new Thread[3];

        for (int i = 0; i < threads.Length; i++)
        {
            int threadNum = i + 1;
            threads[i] = new Thread(() => DoWork(threadNum, "Thread"));
            threads[i].Name = $"Worker{threadNum}";
            threads[i].Start();
        }

        // 等待所有线程完成
        foreach (var thread in threads)
        {
            thread.Join();
        }

        Console.WriteLine("所有线程工作完成");
    }

    private static void DoWork(int id, string type)
    {
        Console.WriteLine($"{type} {id} 开始工作 - 线程ID: {Thread.CurrentThread.ManagedThreadId}");

        // 模拟工作
        for (int i = 1; i <= 3; i++)
        {
            Thread.Sleep(500);
            Console.WriteLine($"{type} {id} 完成步骤 {i}");
        }

        Console.WriteLine($"{type} {id} 工作完成");
    }
}

Task 异步编程示例

using System;
using System.Threading;
using System.Threading.Tasks;

public class TaskAsyncExample
{
    public static void TaskAsyncDemo()
    {
        Console.WriteLine("\n=== Task 异步编程演示 ===");
        Console.WriteLine($"主线程ID: {Thread.CurrentThread.ManagedThreadId}");

        // 创建多个 Task
        Task[] tasks = new Task[3];

        for (int i = 0; i < tasks.Length; i++)
        {
            int taskNum = i + 1;
            tasks[i] = Task.Run(() => DoWork(taskNum, "Task"));
        }

        // 等待所有 Task 完成
        Task.WaitAll(tasks);

        Console.WriteLine("所有 Task 工作完成");
    }

    private static void DoWork(int id, string type)
    {
        Console.WriteLine($"{type} {id} 开始工作 - 线程ID: {Thread.CurrentThread.ManagedThreadId}");
        Console.WriteLine($"{type} {id} 是线程池线程: {Thread.CurrentThread.IsThreadPoolThread}");

        // 模拟工作
        for (int i = 1; i <= 3; i++)
        {
            Thread.Sleep(500);
            Console.WriteLine($"{type} {id} 完成步骤 {i}");
        }

        Console.WriteLine($"{type} {id} 工作完成");
    }
}

性能对比示例

public class PerformanceComparison
{
    public static void ComparePerformance()
    {
        Console.WriteLine("\n=== 性能对比 ===");

        // 测试线程创建性能
        TestThreadPerformance();

        // 测试 Task 性能
        TestTaskPerformance();
    }

    private static void TestThreadPerformance()
    {
        Console.WriteLine("测试传统线程性能:");
        var stopwatch = System.Diagnostics.Stopwatch.StartNew();

        const int count = 50;
        var threads = new Thread[count];

        for (int i = 0; i < count; i++)
        {
            int threadId = i;
            threads[i] = new Thread(() =>
            {
                // 简单计算
                int sum = 0;
                for (int j = 0; j < 1000; j++)
                {
                    sum += j;
                }
            });
            threads[i].Start();
        }

        foreach (var thread in threads)
        {
            thread.Join();
        }

        stopwatch.Stop();
        Console.WriteLine($"创建 {count} 个线程耗时: {stopwatch.ElapsedMilliseconds} ms");
    }

    private static void TestTaskPerformance()
    {
        Console.WriteLine("测试 Task 性能:");
        var stopwatch = System.Diagnostics.Stopwatch.StartNew();

        const int count = 50;
        var tasks = new Task[count];

        for (int i = 0; i < count; i++)
        {
            int taskId = i;
            tasks[i] = Task.Run(() =>
            {
                // 简单计算
                int sum = 0;
                for (int j = 0; j < 1000; j++)
                {
                    sum += j;
                }
            });
        }

        Task.WaitAll(tasks);

        stopwatch.Stop();
        Console.WriteLine($"创建 {count} 个 Task 耗时: {stopwatch.ElapsedMilliseconds} ms");
    }
}

I/O 操作对比

public class IOOperationComparison
{
    public static void CompareIOOperations()
    {
        Console.WriteLine("\n=== I/O 操作对比 ===");

        // 传统线程处理文件 I/O
        ThreadFileIO();

        // Task 处理文件 I/O
        TaskFileIO();
    }

    private static void ThreadFileIO()
    {
        Console.WriteLine("传统线程处理文件 I/O:");

        string[] filePaths = {
            Path.GetTempFileName(),
            Path.GetTempFileName(),
            Path.GetTempFileName()
        };

        var threads = new Thread[filePaths.Length];

        for (int i = 0; i < filePaths.Length; i++)
        {
            int fileIndex = i;
            threads[i] = new Thread(() => ProcessFile(filePaths[fileIndex], fileIndex + 1, "Thread"));
            threads[i].Start();
        }

        foreach (var thread in threads)
        {
            thread.Join();
        }

        // 清理文件
        foreach (var path in filePaths)
        {
            if (File.Exists(path))
                File.Delete(path);
        }
    }

    private static void TaskFileIO()
    {
        Console.WriteLine("Task 处理文件 I/O:");

        string[] filePaths = {
            Path.GetTempFileName(),
            Path.GetTempFileName(),
            Path.GetTempFileName()
        };

        var tasks = new Task[filePaths.Length];

        for (int i = 0; i < filePaths.Length; i++)
        {
            int fileIndex = i;
            tasks[i] = Task.Run(() => ProcessFile(filePaths[fileIndex], fileIndex + 1, "Task"));
        }

        Task.WaitAll(tasks);

        // 清理文件
        foreach (var path in filePaths)
        {
            if (File.Exists(path))
                File.Delete(path);
        }
    }

    private static void ProcessFile(string filePath, int fileNumber, string type)
    {
        Console.WriteLine($"{type} 处理文件 {fileNumber} 开始");

        // 写入文件
        for (int i = 0; i < 3; i++)
        {
            File.AppendAllText(filePath, $"{type} File {fileNumber} - Line {i + 1}\n");
            Thread.Sleep(100); // 模拟 I/O 延迟
        }

        // 读取文件
        string content = File.ReadAllText(filePath);
        Console.WriteLine($"{type} 处理文件 {fileNumber} 完成,内容长度: {content.Length}");
    }
}

错误处理对比

public class ErrorHandlingComparison
{
    public static void CompareErrorHandling()
    {
        Console.WriteLine("\n=== 错误处理对比 ===");

        // 传统线程错误处理
        ThreadErrorHandling();

        // Task 错误处理
        TaskErrorHandling();
    }

    private static void ThreadErrorHandling()
    {
        Console.WriteLine("传统线程错误处理:");

        var thread = new Thread(() =>
        {
            try
            {
                throw new InvalidOperationException("线程中的异常");
            }
            catch (Exception ex)
            {
                Console.WriteLine($"线程内捕获异常: {ex.Message}");
                // 异常必须在线程内部处理
            }
        });

        thread.Start();
        thread.Join();

        Console.WriteLine("线程错误处理完成");
    }

    private static void TaskErrorHandling()
    {
        Console.WriteLine("Task 错误处理:");

        var task = Task.Run(() =>
        {
            throw new InvalidOperationException("Task 中的异常");
        });

        try
        {
            task.Wait(); // 异常会传播到这里
        }
        catch (AggregateException ex)
        {
            Console.WriteLine($"Task 异常传播: {ex.InnerException?.Message}");
        }

        Console.WriteLine("Task 错误处理完成");
    }
}

综合演示

public class ComprehensiveDemo
{
    public static void RunAllDemos()
    {
        Console.WriteLine("多线程与异步编程对比演示");
        Console.WriteLine("=".PadRight(50, '='));

        // 1. 基本使用对比
        MultiThreadingExample.TraditionalThreadingDemo();
        TaskAsyncExample.TaskAsyncDemo();

        // 2. 性能对比
        PerformanceComparison.ComparePerformance();

        // 3. I/O 操作对比
        IOOperationComparison.CompareIOOperations();

        // 4. 错误处理对比
        ErrorHandlingComparison.CompareErrorHandling();

        Console.WriteLine("\n所有演示完成");
    }
}

使用场景指南

public class UsageGuidelines
{
    public static void PrintGuidelines()
    {
        Console.WriteLine("\n=== 使用场景指南 ===");

        Console.WriteLine("推荐使用传统线程的场景:");
        Console.WriteLine("• 需要精确控制线程属性(优先级、栈大小等)");
        Console.WriteLine("• 长期运行的后台服务");
        Console.WriteLine("• 需要线程本地存储(TLS)");
        Console.WriteLine("• 与底层系统交互");

        Console.WriteLine("\n推荐使用 Task 的场景:");
        Console.WriteLine("• 短期运行的并发任务");
        Console.WriteLine("• I/O 密集型操作");
        Console.WriteLine("• 需要返回值的异步操作");
        Console.WriteLine("• 现代 Web 应用和 UI 应用");
        Console.WriteLine("• 需要组合多个异步操作");

        Console.WriteLine("\n性能考虑:");
        Console.WriteLine("• Task 利用线程池,减少线程创建开销");
        Console.WriteLine("• 大量短期任务使用 Task 更高效");
        Console.WriteLine("• 线程适合长期运行的独立任务");
        Console.WriteLine("• Task 在高并发场景下表现更好");
    }
}

总结

关键差异

  1. 资源效率:Task 使用线程池,资源利用率更高
  2. 编程模型:Task 提供更高级的抽象和更简洁的 API
  3. 错误处理:Task 支持异常传播,错误处理更灵活
  4. 组合能力:Task 支持链式操作和复杂的组合模式

选择原则

  • CPU 密集型:短期任务用 Task,长期任务可考虑专用线程
  • I/O 密集型:优先选择 Task 和异步模式
  • 控制需求:需要精确控制时选择传统线程
  • 现代应用:推荐使用 Task 和 async/await 模式

发展趋势

  • .NET 现代应用推荐使用 Task-based Asynchronous Pattern (TAP)
  • async/await 关键字进一步简化异步编程
  • 传统线程主要用于特殊场景和底层开发
  • 混合使用:根据具体需求选择合适的并发模型
posted @ 2025-08-27 01:24  jessqiu  阅读(110)  评论(0)    收藏  举报