LOGO OA教程 ERP教程 模切知识交流 PMS教程 CRM教程 开发文档 其他文档  
 
网站管理员

C# 中 Monitor 的多线程应用

admin
2025年1月26日 9:35 本文热度 354

在C#的多线程编程中,Monitor 是一种用于同步多个线程访问共享资源的机制。它是基于对象的锁定机制,能够有效地控制对代码块的访问,防止数据的不一致,其实与lock基本一样的。本文将详细介绍 Monitor 的特点、用法,并提供多个示例以展示其应用。

`Monitor` 的特点

  • 独占性访问Monitor 通过锁定对象,确保同一时刻只有一个线程可以访问被锁定的代码块。

  • 高效性:相比于 MutexMonitor 的性能开销较小,适合在同一进程中的多线程环境中使用。

  • 支持条件变量Monitor 允许线程在等待某个条件时释放锁,这样其他线程可以获得锁,避免资源的浪费。

  • 易于使用Monitor 提供了较为简单的 APIs,如 EnterExitWaitPulse 和 PulseAll

使用 `Monitor` 的基本语法

以下是 Monitor 的基本用法示例:

object lockObject = new object();

Monitor.Enter(lockObject); // 请求锁
try {
    // ... 访问共享资源
} finally {
    Monitor.Exit(lockObject); // 释放锁
}

示例:使用 `Monitor` 实现线程安全的计数器

以下示例展示了如何使用 Monitor 来实现一个线程安全的计数器,确保只有一个线程可以对计数器进行更新。

namespace AppMonitor01
{
    internal class Program
    {

        private static int counter = 0// 共享资源
        private static object lockObject = new object(); // 用于锁定代码块

        static void Main(string[] args)
        
{
            Thread[] threads = new Thread[5];
            for (int i = 0; i < threads.Length; i++)
            {
                threads[i] = new Thread(IncrementCounter);
                threads[i].Start();
            }

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

            Console.WriteLine($"最终计数器的值: {counter}");
        }

        static void IncrementCounter()
        
{
            for (int i = 0; i < 1000; i++)
            {
                Monitor.Enter(lockObject); // 请求锁
                try
                {
                    counter++; // 增加计数
                }
                finally
                {
                    Monitor.Exit(lockObject); // 确保释放锁
                }
            }
        }
    }
}

代码解析

  1. 共享资源counter 是一个静态变量,由多个线程共享。

  2. 锁对象lockObject 是一个用于同步的对象,所有线程通过该对象进行控制。

  3. 线程创建和启动:创建5个线程并启动它们,每个线程执行 IncrementCounter 方法。

  4. 计数器增加:在 Monitor.Enter(lockObject) 后,只有获得锁的线程才能执行 counter++

  5. 锁的释放:在 finally 块内调用 Monitor.Exit(lockObject),确保锁总是释放,即使发生异常。

  6. 等待线程完成:主线程使用 Join 方法确保所有工作线程完成后再输出 counter 的值。

示例:使用 `Monitor` 的条件变量

使用 Monitor 的条件变量,允许线程在特定条件下等待以释放锁,以下示例展示了如何使用 Monitor.Wait 和 Monitor.Pulse

using System;
using System.Threading;

class Program {
    private static object lockObject = new object(); // 用于同步的对象
    private static bool isReady = false// 条件变量

    static void Main(string[] args) {
        Thread workerThread = new Thread(Worker);
        Thread notifierThread = new Thread(Notifier);

        workerThread.Start();
        notifierThread.Start();

        workerThread.Join();
        notifierThread.Join();
    }

    static void Worker() {
        Console.WriteLine("Worker 正在等待通知...");
        lock (lockObject) {
            while (!isReady) {
                Monitor.Wait(lockObject); // 等待通知
            }
            Console.WriteLine("Worker 收到通知,开始工作。");
        }
    }

    static void Notifier() {
        Console.WriteLine("Notifier 正在处理...");
        Thread.Sleep(2000); // 模拟处理时间
        lock (lockObject) {
            isReady = true;
            Monitor.Pulse(lockObject); // 发送通知
            Console.WriteLine("Notifier 已发送通知。");
        }
    }
}

代码解析

  1. 条件变量isReady 是一个布尔变量,用于控制 Worker 线程的执行。

  2. 工作线程Worker 线程在获得锁后,检查 isReady 的值,如果为 false,则调用 Monitor.Wait 方法,释放锁并等待通知。

  3. 通知线程Notifier 线程在处理完毕后,通过 Monitor.Pulse 发送通知,唤醒等待的 Worker 线程。

  4. 工作执行:一旦 Worker 收到通知,就会继续执行相应的工作。

示例:使用 `Monitor.PulseAll` 进行唤醒所有等待线程

在某些情况下,可能需要唤醒所有等待的线程,可以使用 Monitor.PulseAll 方法。以下示例展示了如何使用 Monitor.PulseAll

using System;
using System.Threading;

class Program {
    private static object lockObject = new object();
    private static int readyCount = 0// 准备好的线程计数
    private static int neededCount = 3// 需要满多少个线程

    static void Main(string[] args) {
        for (int i = 0; i < 5; i++) {
            Thread thread = new Thread(Worker);
            thread.Start(i + 1);
        }

        Thread.Sleep(2000); // 等待一些时间让所有线程开始
        NotifyThreads(); // 调用通知方法
    }

    static void Worker(object index) {
        Console.WriteLine($"线程 {index} 正在准备...");
        lock (lockObject) {
            readyCount++;
            if (readyCount < neededCount) {
                Monitor.Wait(lockObject); // 等待通知
            }
            Console.WriteLine($"线程 {index} 继续执行。");
        }
    }

    static void NotifyThreads() {
        Console.WriteLine("准备好所有线程。");
        lock (lockObject) {
            // 使得所有等待的线程都能继续
            Monitor.PulseAll(lockObject); 
        }
    }
}

代码解析

  1. 准备计数readyCount 用于计算已准备好的线程数量,而 neededCount 定义了需要满的线程数量。

  2. 工作线程:每个 Worker 在线程启动时增加 readyCount 计数,如果未达到需要数量的线程,则调用 Monitor.Wait 进入等待状态。

  3. 通知方法:当 NotifyThreads 被调用时,会使用 Monitor.PulseAll 唤醒所有等待的线程,使它们能够继续执行。

使用 `Monitor` 的注意事项

  • 确保释放锁:始终使用 try…finally 構造以确保在异常或早期返回时仍然释放锁。

  • 避免死锁:在使用 Monitor 时,确保不以不当顺序获取多个锁,避免死锁现象。

  • 高并发下的性能:虽然 Monitor 的性能相对较好,但在高并发情况下,可能仍会造成性能瓶颈。

  • 条件变量的使用:使用 Monitor.Wait 和 Monitor.Pulse 时,确保它们在相同的锁定对象上调用,以确保正确执行。

结论

Monitor 是C#中一种强大的多线程同步机制,能够有效管理线程对共享资源的访问。通过上述示例,您可以看到如何利用 Monitor 来实现线程安全的操作,并控制线程的执行顺序。合理地使用 Monitor 可以显著提高多线程应用的可靠性和效率。在应用中,开发者应关注性能、错误处理及可能的竞争条件,以确保安全和高效性。


该文章在 2025/1/26 9:35:08 编辑过
关键字查询
相关文章
正在查询...
点晴ERP是一款针对中小制造业的专业生产管理软件系统,系统成熟度和易用性得到了国内大量中小企业的青睐。
点晴PMS码头管理系统主要针对港口码头集装箱与散货日常运作、调度、堆场、车队、财务费用、相关报表等业务管理,结合码头的业务特点,围绕调度、堆场作业而开发的。集技术的先进性、管理的有效性于一体,是物流码头及其他港口类企业的高效ERP管理信息系统。
点晴WMS仓储管理系统提供了货物产品管理,销售管理,采购管理,仓储管理,仓库管理,保质期管理,货位管理,库位管理,生产管理,WMS管理系统,标签打印,条形码,二维码管理,批号管理软件。
点晴免费OA是一款软件和通用服务都免费,不限功能、不限时间、不限用户的免费OA协同办公管理系统。
Copyright 2010-2025 ClickSun All Rights Reserved