01、线程基础

线程基础

1. 启动线程的基本语法

在C#中,启动线程主要通过System.Threading.Thread类实现。线程可以执行无参数或带参数的方法,也可以通过lambda表达式定义要执行的代码。

基本语法

  1. 启动无参数方法
using System;
using System.Threading;

class ThreadExample
{
    static void Main()
    {
        // 创建线程,指定要执行的方法
        Thread thread = new Thread(WorkerMethod);
        
        // 启动线程
        thread.Start();
        
        // 主线程继续执行
        Console.WriteLine("主线程正在执行");
        
        // 等待子线程完成(可选)
        thread.Join();
        
        Console.WriteLine("所有线程执行完成");
    }
    
    static void WorkerMethod()
    {
        Console.WriteLine("子线程开始执行");
        // 模拟工作
        Thread.Sleep(1000);
        Console.WriteLine("子线程执行完成");
    }
}
  1. 启动带参数的方法
using System;
using System.Threading;

class ThreadWithParameterExample
{
    static void Main()
    {
        // 创建线程,指定带参数的方法
        Thread thread = new Thread(WorkerWithParameter);
        
        // 启动线程并传递参数
        thread.Start("线程参数");
        
        // 等待线程完成
        thread.Join();
    }
    
    // 参数必须是object类型
    static void WorkerWithParameter(object data)
    {
        string message = data as string;
        if (!string.IsNullOrEmpty(message))
        {
            Console.WriteLine($"接收到参数: {message}");
        }
        Thread.Sleep(1000);
    }
}
  1. 使用Lambda表达式
using System;
using System.Threading;

class ThreadWithLambda
{
    static void Main()
    {
        // 使用lambda表达式定义线程要执行的代码
        Thread thread = new Thread(() => 
        {
            Console.WriteLine("Lambda线程开始");
            Thread.Sleep(1000);
            Console.WriteLine("Lambda线程结束");
        });
        
        thread.Start();
        thread.Join();
    }
}

线程的常用属性和方法

  • Start(): 启动线程
  • Join(): 阻塞调用线程,直到该线程完成
  • Sleep(int millisecondsTimeout): 使线程暂停指定的毫秒数
  • IsAlive: 获取一个值,指示当前线程是否处于活动状态
  • Priority: 设置线程的调度优先级
  • Name: 获取或设置线程的名称

2. 为何使用线程实现分而治之

分而治之(Divide and Conquer)是一种算法设计策略,它将复杂问题分解为 smaller 子问题,并行解决这些子问题,最后合并结果。使用线程实现分而治之有以下优势:

  1. 提高性能:充分利用多核CPU的计算能力
  2. 简化设计:每个子问题可以独立处理,逻辑更清晰
  3. 更好的资源利用:避免单个核心满载而其他核心闲置

分而治之的线程实现案例:数组求和

using System;
using System.Threading;

class ArraySumWithThreads
{
    private static int[] _array;
    private static long _totalSum;
    private static readonly object _lock = new object();

    static void Main()
    {
        // 创建一个大数组
        _array = new int[1000000];
        Random rand = new Random();
        for (int i = 0; i < _array.Length; i++)
        {
            _array[i] = rand.Next(1, 10);
        }

        Console.WriteLine("开始计算数组总和...");
        DateTime startTime = DateTime.Now;

        // 分而治之:将数组分成4个部分,用4个线程计算
        int segmentSize = _array.Length / 4;
        Thread[] threads = new Thread[4];

        for (int i = 0; i < 4; i++)
        {
            int start = i * segmentSize;
            int end = (i == 3) ? _array.Length : start + segmentSize;
            
            threads[i] = new Thread(() => CalculateSum(start, end));
            threads[i].Start();
        }

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

        TimeSpan elapsed = DateTime.Now - startTime;
        Console.WriteLine($"数组总和: {_totalSum}");
        Console.WriteLine($"计算耗时: {elapsed.TotalMilliseconds} 毫秒");
    }

    static void CalculateSum(int start, int end)
    {
        long sum = 0;
        for (int i = start; i < end; i++)
        {
            sum += _array[i];
        }

        // 线程安全地累加结果
        lock (_lock)
        {
            _totalSum += sum;
        }
    }
}

分而治之的适用场景

  • 大型数据集的处理(排序、搜索、统计)
  • 数学计算(矩阵运算、数值积分)
  • 递归问题(斐波那契数列、快速排序)
  • 图像处理(对图像的不同区域进行并行处理)

3. 为何使用线程卸载长时间运行的任务

在应用程序中,长时间运行的任务(如下载大文件、复杂计算、数据库查询)如果在主线程执行,会导致应用程序界面冻结,用户体验下降。使用线程卸载这些任务有以下好处:

  1. 保持UI响应性:主线程可以继续处理用户输入和界面更新
  2. 提高用户体验:用户可以进行其他操作或看到进度反馈
  3. 资源优化:可以在任务执行时释放或重新分配资源
  4. 可取消性:允许用户取消长时间运行的操作

卸载长时间运行任务的案例:文件下载器

using System;
using System.Threading;

class FileDownloader
{
    private bool _isDownloading;
    private bool _isCancelled;
    private Thread _downloadThread;

    public void StartDownload(string url)
    {
        if (_isDownloading)
        {
            Console.WriteLine("已经在下载中...");
            return;
        }

        _isDownloading = true;
        _isCancelled = false;

        Console.WriteLine("开始下载...");
        
        // 在新线程中启动下载
        _downloadThread = new Thread(() => DownloadFile(url));
        _downloadThread.IsBackground = true; // 设为后台线程,程序退出时自动终止
        _downloadThread.Start();

        // 主线程可以继续响应用户输入
        MonitorDownloadProgress();
    }

    private void DownloadFile(string url)
    {
        // 模拟文件下载过程
        for (int i = 0; i <= 100; i += 5)
        {
            if (_isCancelled)
            {
                Console.WriteLine("\n下载已取消");
                _isDownloading = false;
                return;
            }

            // 模拟下载进度
            Thread.Sleep(500); // 模拟网络延迟
            
            // 这里应该使用线程安全的方式更新进度
            lock (this)
            {
                Progress = i;
            }
        }

        Console.WriteLine("\n下载完成!");
        _isDownloading = false;
    }

    public int Progress { get; private set; }

    public void CancelDownload()
    {
        _isCancelled = true;
    }

    private void MonitorDownloadProgress()
    {
        // 主线程监控下载进度并响应用户输入
        while (_isDownloading)
        {
            Console.Write($"\r当前进度: {Progress}% (按C取消)");
            
            if (Console.KeyAvailable)
            {
                ConsoleKeyInfo key = Console.ReadKey(true);
                if (key.Key == ConsoleKey.C)
                {
                    CancelDownload();
                    break;
                }
            }
            
            Thread.Sleep(100);
        }
    }

    static void Main()
    {
        FileDownloader downloader = new FileDownloader();
        downloader.StartDownload("https://example.com/largefile.zip");
    }
}

长时间运行任务的常见类型

  • 网络操作(文件下载、API调用)
  • 数据库操作(复杂查询、批量插入)
  • 计算密集型任务(数据分析、建模、图像处理)
  • I/O操作(大文件读写、备份)

总结

  1. 线程启动语法:

    • 使用Thread类创建线程,通过Start()方法启动
    • 支持无参数方法、带参数方法和lambda表达式
    • 可通过Join()方法等待线程完成,Sleep()方法暂停线程
  2. 分而治之的线程应用:

    • 将大问题分解为小问题,并行处理提高效率
    • 充分利用多核CPU,适合数据密集型任务
    • 需要注意线程间的数据同步和结果合并
  3. 长时间运行任务的线程卸载:

    • 保持主线程(尤其是UI线程)的响应性
    • 提供更好的用户体验和操作灵活性
    • 需实现进度报告和取消机制
    • 通常使用后台线程避免影响程序退出

线程是实现并行计算的基础,但也带来了复杂性,如同步问题、死锁风险和调试难度。在实际开发中,还可以考虑使用更高级的抽象,如Task、Parallel类等,它们提供了更简洁的编程模型和更好的性能控制。

posted @ 2025-08-27 01:24  jessqiu  阅读(15)  评论(0)    收藏  举报