C#多线程编程中的锁系统基本用法

接下来就为大家详细讲解C#多线程编程中的锁系统基本用法的完整攻略。

什么是锁(Lock)

锁用于多线程编程中,是一种用于避免竞争访问共享资源的同步机制。在多线程程序中,多个线程可能同时访问同一个共享资源,如果不采取任何措施,就会产生“竞态条件”,导致程序出现不可预期问题。

为了避免这种情况,我们可以引入锁机制,来确保只有一个线程能够同时访问共享资源,从而避免问题的出现。

.NET Framework提供了一些锁的实现,比如Monitor、Mutex和Semaphore等,本文就以Monitor为例,介绍C#多线程编程中的锁系统的基本用法。

Monitor的基本用法

Monitor是C#中最常用的锁实现之一,可以用于确保多个线程访问同一个共享资源时的同步。

Monitor的主要用法有以下2个:

1.获取对象锁

调用Monitor.Enter方法可以获取某个对象的独占锁,该方法会阻塞当前线程,直到获取该对象的独占锁,代码示例:

class Program {
    static readonly object obj = new object();

    static void Main(string[] args) {
        Console.WriteLine("Main Thread starting...");
        Thread thread = new Thread(DoSomeWork);
        thread.Start();

        lock (obj) {
            Console.WriteLine("Thread A Acquires the lock...");
            Console.WriteLine("Thread A is executing some code...");
            Thread.Sleep(5000);
            Console.WriteLine("Thread A Releasing the lock...");
        }

        Console.WriteLine("Main Thread Exiting...");
    }

    static void DoSomeWork() {
        Console.WriteLine("Thread B Trying to acquire lock...");
        lock (obj) {
            Console.WriteLine("Thread B Acquires the lock...");
            Console.WriteLine("Thread B is executing some code...");
            Thread.Sleep(5000);
            Console.WriteLine("Thread B Releasing the lock...");
        }
    }
}

在上面的示例代码中,我们使用了lock语句获取了obj对象的锁,从而防止了线程A和线程B同时访问obj对象的问题。

注意,在线程A访问代码块时,线程B是无法访问该代码块的,因为lock语句获取的是对象锁,而不是线程锁,只要其他线程试图获取相同的对象锁,就会被阻塞。

2.释放对象锁

通过Monitor.Exit方法可以释放Monitor.Enter所获取的对象锁,代码示例:

class Program {
    static readonly object obj = new object();

    static void Main(string[] args) {
        Console.WriteLine("Main Thread starting...");
        Thread thread = new Thread(DoSomeWork);
        thread.Start();

        lock (obj) {
            Console.WriteLine("Thread A Acquires the lock...");
            Console.WriteLine("Thread A is executing some code...");
            Thread.Sleep(5000);
            Console.WriteLine("Thread A Releasing the lock...");
            Monitor.Pulse(obj);
        }

        Console.WriteLine("Main Thread Exiting...");
    }

    static void DoSomeWork() {
        lock (obj) {
            Console.WriteLine("Thread B Trying to acquire lock...");
            Monitor.Wait(obj);
            Console.WriteLine("Thread B Acquires the lock...");
            Console.WriteLine("Thread B is executing some code...");
            Thread.Sleep(5000);
            Console.WriteLine("Thread B Releasing the lock...");
        }
    }
}

在上面的代码中,我们在获取对象锁之后,调用了Monitor.Pulse方法通知等待该锁的线程(这里是线程B)可以开始执行了。

另外,我们在线程B中使用了Monitor.Wait(obj)方法,将该线程放到等待队列中,等到Monitor.Pulse(obj)被调用时,才会重新尝试获取obj对象的锁。

示范

接下来我们结合一个生产者消费者的实例来看看Monitor的用法。

/// <summary>
/// 产品的模型类
/// </summary>
public class Product {
    /// <summary>
    /// 产品的名称
    /// </summary>
    public string ProductName { get; set; }
}

class Program {
    static readonly object lockObj = new object();
    static Queue<Product> products = new Queue<Product>();

    static void Main(string[] args) {
        new Thread(Producer).Start();
        new Thread(Consumer).Start();

        Console.ReadLine();
    }

    /// <summary>
    /// 生产者方法
    /// </summary>
    static void Producer() {
        while (true) {
            lock (lockObj) {
                while (products.Count >= 10) {
                    Monitor.Wait(lockObj);  //队列已满,等待消费者消费产品
                }

                Product product = new Product {
                    ProductName = Guid.NewGuid().ToString()
                };

                products.Enqueue(product);
                Console.WriteLine($"添加一个产品,目前产品数量:{products.Count}");

                if (products.Count == 1) {
                    Monitor.Pulse(lockObj); //通知消费者有产品可以消费
                }
            }
        }
    }

    /// <summary>
    /// 消费者方法
    /// </summary>
    static void Consumer() {
        while (true) {
            lock (lockObj) {
                while (products.Count == 0) {
                    Monitor.Wait(lockObj); //队列已空,等待生产者生产产品
                }

                Product product = products.Dequeue();
                Console.WriteLine($"消费一个产品,目前产品数量:{products.Count}");

                if (products.Count == 9) {
                    Monitor.Pulse(lockObj); //通知生产者可以继续生产产品
                }
            }
        }
    }
}

上述代码中,由于产品队列为共享资源,producer和consumer都需要操作该队列,因此需要使用lock进行同步,以避免竞争访问。

同时,producer生产产品时,如果队列已满,需要等待消费者消费产品后再继续生产(Monitor.Wait);consumer消费产品时,如果队列为空,需要等待生产者生产产品后再消费(Monitor.Wait)。

在生产者生产产品后,会通过Monitor.Pulse方法通知消费者可以消费产品了,在消费者消费产品后,会通过Monitor.Pulse方法通知生产者可以继续生产产品了。这样,就实现了生产者与消费者之间的同步。

到这里,我们就介绍了C#多线程编程中锁的基本用法,主要包括获取对象锁和释放对象锁。同时结合生产者消费者的实例,为大家演示了如何使用Monitor实现线程同步。

希望大家能够掌握锁的基本用法,并且能够合理运用在自己的多线程程序中。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:C#多线程编程中的锁系统基本用法 - Python技术站

(0)
上一篇 2023年5月15日
下一篇 2023年5月15日

相关文章

  • C#打印PDF文档的10种方法(小结)

    下面我将为您详细讲解“C#打印PDF文档的10种方法(小结)”的完整攻略。 1. 概述 在C#中打印PDF文档可以用多种方法,本文将介绍10种常用的方法,并举例说明,帮助开发者更好的理解。 2. iTextSharp库 iTextSharp是一个流行的开源PDF开发库,可以用C#和VB.NET编写PDF文件。它不仅可以创建PDF文件,还可以读取、编辑、添加注…

    C# 2023年6月1日
    00
  • C#的path.GetFullPath 获取上级目录实现方法

    下面就是使用C#中的Path类的GetFullPath方法获取上级目录的实现方法。 1. 基本用法 Path.GetFullPath方法可以将相对路径转换为绝对路径,同时也可以获取当前路径的完整路径。 下面是示例代码: string path = "../example.txt"; string fullPath = Path.GetFu…

    C# 2023年6月1日
    00
  • C# 使用PictureBox实现图片按钮控件的示例步骤

    下面为大家分享C#使用PictureBox实现图片按钮控件的示例步骤: 步骤1:添加PictureBox控件 在窗体中添加一个PictureBox控件,在该控件的属性中设置Image属性为要作为按钮的图片文件。在此基础上,可以对控件进行进一步设置如:SizeMode等。 步骤2:实现按钮效果 实现按钮效果需要通过各种事件来实现。可以使用MouseEnter和…

    C# 2023年6月7日
    00
  • Asp.Mvc 2.0用户客户端验证实例讲解(3)

    Asp.Mvc 2.0用户客户端验证实例讲解是一篇教程文章,介绍了如何使用Asp.Mvc 2.0实现用户客户端验证。下面是Asp.Mvc 2.0用户客户端验证实例讲解的完整攻略。 1. 概述 本文将介绍如何使用Asp.Mvc 2.0实现用户客户端验证,在前后端分离开发中,用户客户端验证是非常重要的,可以在一定程度上减少请求次数,提高用户体验,同时还可以避免一…

    C# 2023年5月31日
    00
  • C#实现多线程的Web代理服务器实例

    当实现一个Web代理服务器时,需要考虑到多线程的实现,因为同时需要处理多个请求和响应。下面是一个基于C#的多线程Web代理服务器的实现攻略: 1. 开发环境 为了开发一个C#多线程Web代理服务器,需要安装如下软件: Visual Studio:用于编写C#代码和构建Web代理服务器。 .NET Framework:Web代理服务器运行所需的软件框架。 2.…

    C# 2023年5月15日
    00
  • 关于C#连接FTP时路径问题的解决方法

    下面是关于C#连接FTP时路径问题的解决方法的完整攻略。 1. 确定FTP服务器根目录 在连接FTP时,首先需要确定FTP服务器的根目录。通常来说,FTP服务器的根目录可能与本地文件系统的根目录不同,因此需要确保路径的正确性。 假设FTP服务器的根目录为/,则需要使用类似于以下的代码来连接FTP服务器: FtpWebRequest request = (Ft…

    C# 2023年5月15日
    00
  • C#多线程编程中的锁系统(三)

    当多个线程同时访问共享资源时,可能会导致数据的不一致性,从而影响程序的正确性和稳定性。为了解决这个问题,我们可以使用锁系统来保证共享资源在同一时刻只能被一个线程访问和修改。 在C#多线程编程中,锁系统通常使用lock关键字来实现。下面是使用lock关键字来实现的示例代码: public class Counter { private static objec…

    C# 2023年6月7日
    00
  • C# 迭代器分部类与索引器详情

    让我通过以下标题,详细讲解C#迭代器分部类与索引器的详情: 1. 迭代器分部类 1.1 什么是迭代器 在C#中,迭代器是一种用于简化类似于集合、列表、数组等序列的遍历操作的机制。使用迭代器,我们可以通过简单且易于理解的方式遍历序列,而无需考虑复杂的内部实现。 1.2 什么是迭代器分部类 迭代器分部类是一种特殊的类类型,它可以在多个文件中声明并定义,在编译时会…

    C# 2023年5月31日
    00
合作推广
合作推广
分享本页
返回顶部