位置: 文档库 > C#(.NET) > 文档下载预览

《C#多线程编程.doc》

1. 下载的文档为doc格式,下载后可用word或者wps进行编辑;

2. 将本文以doc文档格式下载到电脑,方便收藏和打印;

3. 下载后的文档,内容与下面显示的完全一致,下载之前请确认下面内容是否您想要的,是否完整.

点击下载文档

C#多线程编程.doc

《C#多线程编程》

在.NET开发中,多线程编程是提升程序性能、优化资源利用的核心技术。通过并行执行任务,开发者可以充分利用多核处理器的计算能力,解决耗时操作(如网络请求、文件I/O、复杂计算)导致的界面卡顿问题。本文将系统讲解C#中的多线程编程模型,涵盖基础线程操作、线程池、异步编程模型(APM/EAP/TAP)、并行扩展(PLINQ/Parallel类)以及线程同步机制,并结合实际案例说明如何安全高效地实现多线程应用。

一、基础线程操作

C#通过`System.Threading`命名空间提供基础的线程支持。创建线程最直接的方式是实例化`Thread`类并传入方法委托。

using System;
using System.Threading;

class Program
{
    static void WorkerMethod(object data)
    {
        Console.WriteLine($"线程 {Thread.CurrentThread.ManagedThreadId} 开始工作,参数: {data}");
        Thread.Sleep(2000); // 模拟耗时操作
        Console.WriteLine($"线程 {Thread.CurrentThread.ManagedThreadId} 完成工作");
    }

    static void Main()
    {
        Console.WriteLine($"主线程ID: {Thread.CurrentThread.ManagedThreadId}");
        
        // 创建线程并传递参数
        Thread thread = new Thread(WorkerMethod);
        thread.Start("测试数据");
        
        // 主线程继续执行
        Console.WriteLine("主线程继续执行其他任务...");
        thread.Join(); // 等待子线程结束
        Console.WriteLine("所有线程执行完毕");
    }
}

上述代码展示了线程的创建、启动和等待。但直接使用`Thread`类存在以下问题:线程创建开销大、难以管理大量线程、缺乏统一的资源回收机制。因此,实际应用中更推荐使用线程池。

二、线程池(ThreadPool)

线程池通过复用已创建的线程来减少线程管理的开销。.NET的线程池会自动调整线程数量,根据系统负载动态分配资源。

using System;
using System.Threading;

class Program
{
    static void TaskMethod(object state)
    {
        Console.WriteLine($"线程池线程 {Thread.CurrentThread.ManagedThreadId} 处理任务,状态: {state}");
        Thread.Sleep(1000);
    }

    static void Main()
    {
        // 向线程池排队任务
        for (int i = 0; i 

线程池适用于短时间、高频率的任务执行,但存在以下限制:无法取消已排队的任务、无法设置线程优先级、难以追踪单个任务的执行状态。对于需要更精细控制的场景,异步编程模型(APM)和任务并行库(TPL)是更好的选择。

三、异步编程模型(APM/EAP/TAP)

.NET提供了三种异步编程模式,其中基于任务的异步模式(TAP)是现代C#开发的首选方案。

1. 传统APM模式(IAsyncResult)

APM通过`BeginXxx`/`EndXxx`方法对实现异步操作,例如文件读取:

using System;
using System.IO;
using System.Threading;

class Program
{
    static void Main()
    {
        FileStream fs = new FileStream("test.txt", FileMode.Open);
        byte[] buffer = new byte[fs.Length];
        
        // 开始异步读取
        IAsyncResult result = fs.BeginRead(buffer, 0, buffer.Length, null, null);
        
        // 主线程可执行其他操作
        Console.WriteLine("主线程继续执行...");
        
        // 等待异步操作完成
        while (!result.IsCompleted)
        {
            Thread.Sleep(100);
        }
        
        // 结束异步操作
        int bytesRead = fs.EndRead(result);
        Console.WriteLine($"读取了 {bytesRead} 字节");
        fs.Close();
    }
}

APM模式需要手动管理状态对象和回调,代码复杂度较高。

2. 基于事件的异步模式(EAP)

EAP通过事件通知异步操作完成,典型代表是`WebClient`类:

using System;
using System.Net;

class Program
{
    static void Main()
    {
        WebClient client = new WebClient();
        client.DownloadStringCompleted += (sender, e) =>
        {
            if (e.Error == null)
            {
                Console.WriteLine($"下载内容长度: {e.Result.Length}");
            }
            else
            {
                Console.WriteLine($"下载错误: {e.Error.Message}");
            }
        };
        
        Console.WriteLine("开始下载...");
        client.DownloadStringAsync(new Uri("https://example.com"));
        
        // 防止主线程退出
        Console.ReadLine();
    }
}

EAP简化了异步编程,但事件驱动的模型在复杂场景下仍显繁琐。

3. 基于任务的异步模式(TAP)

TAP是.NET 4.0引入的现代异步编程模型,通过`async`/`await`关键字实现简洁的异步代码:

using System;
using System.IO;
using System.Net.Http;
using System.Threading.Tasks;

class Program
{
    static async Task DownloadFileAsync(string url, string savePath)
    {
        using (HttpClient client = new HttpClient())
        {
            Console.WriteLine("开始下载...");
            byte[] data = await client.GetByteArrayAsync(url);
            Console.WriteLine($"下载完成,数据大小: {data.Length}");
            
            await File.WriteAllBytesAsync(savePath, data);
            Console.WriteLine("文件保存成功");
        }
    }

    static async Task Main()
    {
        try
        {
            await DownloadFileAsync(
                "https://example.com/sample.txt", 
                "downloaded.txt");
        }
        catch (Exception ex)
        {
            Console.WriteLine($"错误: {ex.Message}");
        }
    }
}

TAP的优势在于:

  • 代码结构与同步代码类似,易于维护
  • 自动处理线程上下文切换
  • 支持异常传播和取消操作
  • 与异步流(IAsyncEnumerable)无缝集成

四、并行扩展(PLINQ与Parallel类)

对于数据并行场景,.NET提供了PLINQ(Parallel LINQ)和`Parallel`类来简化多线程编程。

1. PLINQ示例

using System;
using System.Linq;

class Program
{
    static void Main()
    {
        int[] numbers = Enumerable.Range(1, 1000).ToArray();
        
        // 顺序处理
        var sequentialSum = numbers.Where(n => n % 2 == 0).Sum();
        
        // 并行处理
        var parallelSum = numbers.AsParallel()
                                .Where(n => n % 2 == 0)
                                .Sum();
        
        Console.WriteLine($"顺序计算结果: {sequentialSum}");
        Console.WriteLine($"并行计算结果: {parallelSum}");
    }
}

PLINQ会自动将查询分解为多个任务并在多个线程上执行,但需要注意线程安全问题。

2. Parallel类示例

using System;
using System.Threading.Tasks;

class Program
{
    static void ProcessItem(int item)
    {
        Console.WriteLine($"处理项目 {item} 在线程 {Thread.CurrentThread.ManagedThreadId}");
        Thread.Sleep(100); // 模拟处理时间
    }

    static void Main()
    {
        ParallelOptions options = new ParallelOptions
        {
            MaxDegreeOfParallelism = 4 // 限制最大并发数
        };
        
        Parallel.For(0, 20, options, i =>
        {
            ProcessItem(i);
        });
        
        Console.WriteLine("所有项目处理完成");
    }
}

`Parallel.For`和`Parallel.ForEach`提供了声明式的并行循环,支持取消令牌和分区控制。

五、线程同步机制

多线程编程的核心挑战是共享资源的同步访问。.NET提供了多种同步原语:

1. lock关键字

using System;
using System.Threading;

class Counter
{
    private int _count = 0;
    private readonly object _lockObj = new object();
    
    public void Increment()
    {
        lock (_lockObj)
        {
            _count++;
            Thread.Sleep(10); // 模拟耗时操作
        }
    }
    
    public int Value => _count;
}

class Program
{
    static void Main()
    {
        Counter counter = new Counter();
        
        Parallel.For(0, 100, _ => 
        {
            counter.Increment();
        });
        
        Console.WriteLine($"最终计数: {counter.Value}");
    }
}

`lock`语句通过获取指定对象的互斥锁来确保代码块的原子性执行。

2. Monitor类

`Monitor`提供了比`lock`更底层的控制,支持尝试获取锁和超时机制:

using System;
using System.Threading;

class ResourcePool
{
    private int _available = 5;
    private readonly object _poolLock = new object();
    
    public bool TryAcquire(out int resourceId)
    {
        if (!Monitor.TryEnter(_poolLock, TimeSpan.FromSeconds(1)))
        {
            resourceId = -1;
            return false;
        }
        
        try
        {
            if (_available > 0)
            {
                resourceId = _available--;
                return true;
            }
            resourceId = -1;
            return false;
        }
        finally
        {
            Monitor.Exit(_poolLock);
        }
    }
}

class Program
{
    static void Main()
    {
        ResourcePool pool = new ResourcePool();
        Parallel.For(0, 10, _ =>
        {
            if (pool.TryAcquire(out int id))
            {
                Console.WriteLine($"线程 {Thread.CurrentThread.ManagedThreadId} 获取资源 {id}");
                Thread.Sleep(100);
            }
            else
            {
                Console.WriteLine($"线程 {Thread.CurrentThread.ManagedThreadId} 获取资源失败");
            }
        });
    }
}

3. 信号量(Semaphore)

信号量控制同时访问资源的线程数量:

using System;
using System.Threading;

class ConnectionPool
{
    private Semaphore _semaphore;
    private int _maxConnections;
    
    public ConnectionPool(int maxConnections)
    {
        _maxConnections = maxConnections;
        _semaphore = new Semaphore(maxConnections, maxConnections);
    }
    
    public void UseConnection(Action action)
    {
        _semaphore.WaitOne(); // 获取信号量
        try
        {
            Console.WriteLine($"获取连接(剩余{_semaphore.AvailablePermits})");
            action();
        }
        finally
        {
            _semaphore.Release(); // 释放信号量
        }
    }
}

class Program
{
    static void Main()
    {
        ConnectionPool pool = new ConnectionPool(3);
        
        Parallel.For(0, 10, _ =>
        {
            pool.UseConnection(() =>
            {
                Thread.Sleep(1000);
                Console.WriteLine($"线程 {Thread.CurrentThread.ManagedThreadId} 使用连接");
            });
        });
    }
}

4. 互斥量(Mutex)

互斥量可用于跨进程同步:

using System;
using System.Threading;

class Program
{
    static void Main()
    {
        bool createdNew;
        using (Mutex mutex = new Mutex(true, "Global\\MyAppMutex", out createdNew))
        {
            if (!createdNew)
            {
                Console.WriteLine("程序已在运行中");
                return;
            }
            
            Console.WriteLine("程序正常运行");
            Console.ReadLine();
        }
    }
}

六、最佳实践与常见陷阱

1. 避免过度同步:只在必要时使用同步机制,过多的锁会降低性能

2. 优先使用高级抽象:TPL、PLINQ等通常比手动线程管理更高效

3. 注意线程安全:共享可变状态时必须同步,或使用不可变对象

4. 正确处理异常:异步代码中的异常需要通过`await`传播或`try/catch`捕获

5. 避免死锁:确保锁的获取顺序一致,防止循环等待

6. 考虑任务取消:使用`CancellationToken`实现优雅的任务终止

多线程编程是.NET开发者必须掌握的核心技能。从基础的`Thread`类到现代的`async/await`模式,从简单的锁机制到复杂的并行算法,.NET提供了丰富的工具集来应对各种并发场景。合理运用这些技术可以显著提升应用程序的响应能力和吞吐量,但同时也需要谨慎处理同步问题以避免竞态条件和死锁。随着.NET的持续演进,特别是.NET Core/.NET 5+对跨平台并发模型的支持,多线程编程将继续在高性能计算、实时系统等领域发挥关键作用。

关键词:C#多线程编程、.NET线程池、异步编程、TAP模式、PLINQ、Parallel类、线程同步、lock关键字、信号量、互斥量

简介:本文系统讲解C#多线程编程技术,涵盖基础线程操作、线程池、异步编程模型(APM/EAP/TAP)、并行扩展(PLINQ/Parallel类)以及线程同步机制。通过代码示例说明如何安全高效地实现多线程应用,并总结了最佳实践与常见陷阱。

《C#多线程编程.doc》
将本文以doc文档格式下载到电脑,方便收藏和打印
推荐度:
点击下载文档