C# 线程与同步介绍


.NET学习资料

.NET学习资料

.NET学习资料


在 C# 编程中,线程和同步是实现高效、可靠应用程序的关键概念。随着计算机硬件的发展,多核处理器的普及,充分利用多线程技术可以显著提升应用程序的性能和响应速度。而同步机制则是确保多线程环境下数据一致性和程序正确性的重要手段。

一、C# 线程基础

(一)线程的概念与作用

线程是程序执行的最小单元,它是进程内部的一个执行路径。在一个进程中可以包含多个线程,这些线程共享进程的资源,如内存空间、文件句柄等。通过使用多线程,程序可以同时执行多个任务,从而提高系统的利用率和响应性。例如,在一个图形用户界面(GUI)应用程序中,可以使用一个线程来处理用户界面的交互,另一个线程来执行后台数据加载或计算任务,这样可以避免界面卡顿,提升用户体验。

(二)创建和启动线程

在 C# 中,创建线程非常简单,通过System.Threading.Thread类来实现。以下是一个简单的示例:

cpp 复制代码
using System;
using System.Threading;

class Program
{
    static void Main()
    {
        // 创建一个新线程,传入线程执行的方法
        Thread thread = new Thread(PrintNumbers);
        // 启动线程
        thread.Start();

        // 主线程继续执行其他任务
        for (int i = 10; i < 20; i++)
        {
            Console.WriteLine($"Main thread: {i}");
        }
    }

    static void PrintNumbers()
    {
        for (int i = 0; i < 10; i++)
        {
            Console.WriteLine($"Thread: {i}");
        }
    }
}

在这个示例中,我们创建了一个新线程thread,并将PrintNumbers方法作为线程的执行体。调用thread.Start()方法后,新线程开始执行PrintNumbers方法中的代码,同时主线程继续执行Main方法中的后续代码。

(三)线程的生命周期

线程的生命周期包括以下几个阶段:

新建(New):当使用new关键字创建一个Thread对象时,线程处于新建状态,此时线程还未开始执行。

就绪(Ready):调用Start方法后,线程进入就绪状态,等待 CPU 调度执行。在这个状态下,线程已经具备了执行的条件,但还没有获得 CPU 时间片。

运行(Running):当线程获得 CPU 时间片时,线程进入运行状态,开始执行其关联的方法中的代码。

阻塞(Blocked):在某些情况下,线程可能会进入阻塞状态,例如线程调用了Thread.Sleep方法、等待某个事件发生、获取锁失败等。在阻塞状态下,线程暂时停止执行,直到满足特定的条件后才会重新进入就绪状态。

死亡(Dead):当线程执行完其关联的方法,或者调用了Abort方法终止线程时,线程进入死亡状态,此时线程不再存在。

二、C# 线程同步

(一)线程同步的必要性

在多线程环境下,如果多个线程同时访问和修改共享资源,可能会导致数据不一致和竞态条件(Race Condition)问题。例如,两个线程同时读取一个共享变量的值,然后各自对其进行修改并写回,最终的结果可能取决于线程执行的顺序,这是我们不希望看到的。因此,需要使用线程同步机制来确保在同一时刻只有一个线程能够访问共享资源,从而保证数据的一致性和程序的正确性。

(二)常见的线程同步技术

锁(Lock)

原理 :lock关键字用于创建一个互斥锁,它可以确保在同一时刻只有一个线程能够进入被锁定的代码块。当一个线程进入lock代码块时,它会获取锁,其他线程在试图进入该代码块时会被阻塞,直到获取锁的线程退出lock代码块并释放锁。
示例

cpp 复制代码
class SharedResource
{
    private int _count = 0;
    private readonly object _lockObject = new object();

    public void Increment()
    {
        lock (_lockObject)
        {
            _count++;
            Console.WriteLine($"Count: {_count}");
        }
    }
}

class Program
{
    static void Main()
    {
        SharedResource sharedResource = new SharedResource();
        Thread thread1 = new Thread(() =>
        {
            for (int i = 0; i < 10; i++)
            {
                sharedResource.Increment();
            }
        });
        Thread thread2 = new Thread(() =>
        {
            for (int i = 0; i < 10; i++)
            {
                sharedResource.Increment();
            }
        });

        thread1.Start();
        thread2.Start();

        thread1.Join();
        thread2.Join();
    }
}

在这个示例中,SharedResource类中的Increment方法使用lock关键字来保护对_count变量的访问,确保在多线程环境下_count的修改是安全的。

Monitor 类

原理 :Monitor类提供了更底层的线程同步控制,它包含了Enter、Exit、Wait、Pulse和PulseAll等方法。Enter方法用于获取对象的锁,Exit方法用于释放锁,Wait方法用于使当前线程等待,直到其他线程调用Pulse或PulseAll方法通知它,Pulse方法用于通知等待队列中的一个线程,PulseAll方法用于通知等待队列中的所有线程。
示例

cpp 复制代码
class MonitorExample
{
    private static readonly object _lockObject = new object();
    private static bool _isReady = false;

    public static void ThreadA()
    {
        Monitor.Enter(_lockObject);
        try
        {
            while (!_isReady)
            {
                Monitor.Wait(_lockObject);
            }
            Console.WriteLine("ThreadA is running");
        }
        finally
        {
            Monitor.Exit(_lockObject);
        }
    }

    public static void ThreadB()
    {
        Monitor.Enter(_lockObject);
        try
        {
            _isReady = true;
            Monitor.Pulse(_lockObject);
            Console.WriteLine("ThreadB signaled ThreadA");
        }
        finally
        {
            Monitor.Exit(_lockObject);
        }
    }
}

在这个示例中,ThreadA线程在_isReady为false时调用Monitor.Wait方法进入等待状态,ThreadB线程将_isReady设置为true后调用Monitor.Pulse方法通知ThreadA线程,ThreadA线程收到通知后继续执行。

Mutex(互斥量)

原理 :Mutex(互斥量)与lock类似,但它可以跨进程使用,用于同步不同进程之间的资源访问。Mutex可以确保在同一时刻只有一个线程或进程能够获取到互斥量,从而访问共享资源。
示例

cpp 复制代码
using System.Threading;

class MutexExample
{
    private static readonly Mutex _mutex = new Mutex(false, "MyMutex");

    public static void AccessResource()
    {
        _mutex.WaitOne();
        try
        {
            Console.WriteLine("Thread has acquired the mutex and is accessing the resource");
            // 模拟资源访问操作
            Thread.Sleep(1000);
        }
        finally
        {
            _mutex.ReleaseMutex();
            Console.WriteLine("Thread has released the mutex");
        }
    }
}

在这个示例中,MutexExample类中的AccessResource方法使用Mutex来保护对共享资源的访问,WaitOne方法用于获取互斥量,ReleaseMutex方法用于释放互斥量。

Semaphore(信号量)

原理 :Semaphore(信号量)用于控制同时访问某个资源的线程数量。它维护一个许可计数,线程在访问资源前需要获取一个许可,如果许可计数为零,线程将被阻塞,直到有其他线程释放许可。
示例

cpp 复制代码
using System.Threading;

class SemaphoreExample
{
    private static readonly Semaphore _semaphore = new Semaphore(3, 3);

    public static void AccessResource()
    {
        _semaphore.WaitOne();
        try
        {
            Console.WriteLine("Thread has acquired a semaphore permit and is accessing the resource");
            // 模拟资源访问操作
            Thread.Sleep(1000);
        }
        finally
        {
            _semaphore.Release();
            Console.WriteLine("Thread has released the semaphore permit");
        }
    }
}

在这个示例中,SemaphoreExample类中的AccessResource方法使用Semaphore来限制同时访问资源的线程数量为 3,WaitOne方法用于获取许可,Release方法用于释放许可。

三、线程同步的应用场景

多线程访问共享数据:在多个线程需要访问和修改共享数据时,如数据库连接、共享内存区域等,必须使用线程同步机制来确保数据的一致性和完整性。

生产者 - 消费者模型:在生产者 - 消费者模型中,生产者线程生成数据并放入共享缓冲区,消费者线程从缓冲区中取出数据进行处理。为了避免缓冲区溢出和数据竞争,需要使用线程同步机制来协调生产者和消费者的操作。

资源池管理:例如数据库连接池、线程池等资源池的管理,需要使用线程同步来确保资源的正确分配和回收,避免资源的过度使用和浪费。

通过以上对 C# 线程与同步的介绍,希望能帮助你深入理解这两个重要概念及其在实际开发中的应用。如果你对代码实现细节、原理有进一步的疑问,或者想要了解更多相关应用场景,欢迎随时交流。

相关推荐
CodeClimb13 分钟前
【华为OD-E卷 - 120 分割数组的最大差值 100分(python、java、c++、js、c)】
java·javascript·c++·python·华为od
硕风和炜21 分钟前
【LeetCode: 378. 有序矩阵中第 K 小的元素 + 二分】
java·算法·leetcode·面试·矩阵·二分
我真不会起名字啊21 分钟前
C++开发(软件开发)常见面试题
java·jvm·c++
神秘的t27 分钟前
javaEE初阶————多线程初阶(4)
java·开发语言
南宫生41 分钟前
力扣动态规划-23【算法学习day.117】
java·学习·算法·leetcode·动态规划
陈老师还在写代码1 小时前
安卓开发用Java、Flutter、Kotlin的区别
android·java·flutter
鲤籽鲲1 小时前
C# ManualResetEvent 类 使用详解
java·开发语言·c#·多线程
硬件人某某某1 小时前
微信小程序~电器维修系统小程序
java·ajax·微信小程序·小程序
猿java1 小时前
MySQL 如何实现主从复制?
java·后端·mysql
martian6652 小时前
【Java基础篇】——第4篇:Java常用类库与工具类
java·开发语言