C#信号量用法简单示例

当我们需要对一个或多个资源进行控制时,可以使用信号量。信号量是一种同步原语,它可以被用来跟踪资源的可用性。在这篇文章中,我们将会讲解C#中的信号量用法,包括信号量的基本操作和信号量用法的示例。

基本用法

在C#中,信号量可以通过System.Threading命名空间的Semaphore类来实现。Semaphore类封装了Windows内核对象,可以根据需要增加或删除元素。常用的构造函数有以下两种:

  • Semaphore(int initialCount, int maximumCount):初始化一个 Semaphore 实例,它同时限制了同时访问信号量的线程数量。initialCount参数指定最初可使用的锁定数量;maximumCount指定信号量的最大值,最大值不能小于initialCount值。
  • Semaphore(int initialCount, int maximumCount, string name, out bool createdNew):初始化具有指定名称的 Semaphore 对象,如果 Semaphore 对象不存在,则新建一个。name参数指定 Semaphore 实例的名称,createdNew参数指示是否创建了 Semaphore 实例。

Semaphore类有以下一些常见的方法:

  • WaitOne():尝试减小 Semaphore 对象的可用计数器。如果当前可用的计数器为 0,则会阻止调用线程,直到 Semaphore 可用为止。
  • WaitForMultipleObjects(IntPtr[], bool, int):暂停当前线程,直到在多个等待线程句柄中的一个变为终止状态或等待线程时间到达限制。在接收到一个等待的句柄后,方法会将其他等待的句柄从等待状态下移除。
  • Release():增加 Semaphore 对象计数器的值,从而允许创建者线程使用 Semaphore。

接下来是Semaphore类的一个简单示例:

using System;
using System.Threading;

class SemaphoreDemo
{
    static Semaphore _pool = new Semaphore(0, 2);

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

        Console.ReadLine();
    }

    static void Worker(object id)
    {
        Console.WriteLine("Worker {0} 开始执行...", id);
        _pool.WaitOne();
        Console.WriteLine("Worker {0} 开始使用资源...", id);
        Thread.Sleep(5000);
        Console.WriteLine("Worker {0} 使用资源结束...", id);
        _pool.Release();
    }
}

在这个示例中,Semaphore对象的最大计数器值被设置为2,以限制最多只有两个线程可以同时访问受信号量保护的资源。五个不同的工作线程被创建并启动,它们共享两个信号量。当一个工作线程需要访问资源时,它会等待另一个线程释放一个信号量,然后才能继续执行。

示例1:控制并发访问Web API

一个常见的示例是,你的应用程序需要同步地访问一个Web API。由于API的限制,你最多只能有三个并发连接。为了管理你的连接,你可以使用一个信号量。

using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Threading;

class WebApiManager
{
    static Semaphore _semaphore;

    static WebApiManager()
    {
        _semaphore = new Semaphore(3, 3);
    }

    public static async Task<string> GetDataAsync(string url)
    {
        _semaphore.WaitOne();

        try
        {
            using (var client = new HttpClient())
            {
                var response = await client.GetStringAsync(url);
                return response;
            }
        }
        finally
        {
            _semaphore.Release();
        }
    }
}

在上面的示例中,使用了一个Semaphore对象来限制API调用的并发量。Semaphore对象的初始计数是3,因此最多只能有3个线程同时调用GetDataAsync()方法。在传入url参数之后,线程通过调用Semaphore对象的WaitOne()方法来等待可用的信号量。如果当前没有可用的信号量,线程将被阻塞,直到有一个信号量可用或者超时。当信号量可用时,线程可以继续执行,使用HttpClient发起API调用。当线程完成调用时,它调用信号量的Release()方法来释放一个信号量。

示例2:实现资源池

另一个示例是,你需要管理一个资源池,例如数据库连接、网络连接等。由于这些资源是有限的,你需要一个机制来限制资源的并发使用量。你可以使用一个信号量来实现这个功能。

class ResourcePool<T>
{
    private Stack<T> _resources;
    private Semaphore _semaphore;

    public ResourcePool(IEnumerable<T> resources, int maxCount)
    {
        _resources = new Stack<T>(resources);
        _semaphore = new Semaphore(maxCount, maxCount);
    }

    public T Acquire()
    {
        _semaphore.WaitOne();

        T resource;

        lock (_resources)
        {
            resource = _resources.Pop();
        }

        return resource;
    }

    public void Release(T resource)
    {
        lock (_resources)
        {
            _resources.Push(resource);
        }

        _semaphore.Release();
    }
}

在上面的示例中,使用一个Semaphore对象来限制ResourcePool对象中的资源并发使用量。在构造函数中,你需要指定初始资源列表和Semaphore最大计数器值。在Acquire()方法中,线程等待一个可用的信号量,并且从资源池中弹出一个可用的资源。在Release()方法中,线程将资源放回资源池,并释放一个信号量。

这样,就实现了一个Thread-Safe的资源池,可以在多线程环境下被安全地访问。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:C#信号量用法简单示例 - Python技术站

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

相关文章

  • C# 如何添加错误日志信息

    当我们在开发C#应用程序时,通常需要将错误日志信息输出到一个日志文件中,以便于在应用出现问题时能够及时定位错误并进行跟踪。本文将介绍如何在C#应用程序中添加错误日志信息。 1. 引入命名空间 using System.IO; 2. 创建日志文件 string logFilePath = @"C:\Logs\myLog.txt"; Stre…

    C# 2023年5月15日
    00
  • ASP.NET MVC实现依赖注入的完整过程

    ASP.NET MVC框架中实现依赖注入的步骤如下: 第一步:配置依赖注入容器 依赖注入容器是一个工具,它能够帮助我们在需要使用对象时自动创建、管理和提供这些对象。在ASP.NET MVC中,常用的依赖注入容器有Unity、Autofac等。在这里以Unity为例来进行展示: 首先需要通过NuGet下载安装Unity包,可以使用以下命令: Install-P…

    C# 2023年5月31日
    00
  • C#跨平台开发之使用C/C++生成的动态链接库

    C#跨平台开发时,有时候需要调用C/C++编写的动态链接库(DLL)来完成一些特定功能,这时我们需要使用P/Invoke (Platform Invoke)机制来调用DLL。下面是使用C/C++生成动态链接库供C#跨平台项目调用的完整攻略: 步骤一:创建DLL项目 首先,在Visual Studio中创建一个动态链接库项目,可以选择C++/CLI(DLL)模…

    C# 2023年5月15日
    00
  • C#Process的OutputDataReceived事件不触发问题及解决

    首先需要说明的是,C#中的Process类可以用于启动和管理外部进程,包括可以获取该进程的标准输出流等信息。然而,有时候我们会遇到Process类中OutputDataReceived事件不触发的问题,也就是说并不能获取到进程的标准输出流信息。 出现这个问题的原因有多种,比如: 进程的输出缓冲区被填满; 进程输出数据流的标准输出缓冲区不存在; 异步读取操作运…

    C# 2023年6月6日
    00
  • C#字符串的常用操作工具类代码分享

    我来详细讲解一下“C#字符串的常用操作工具类代码分享”的完整攻略。 一、介绍 随着C#语言的不断发展,对字符串的操作越发重要。本文主要介绍C#中字符串的常用操作工具类代码分享。 二、C#字符串操作示例 1. 字符串转换为大写 string str = "hello world"; string upperStr = str.ToUpper…

    C# 2023年5月31日
    00
  • C#通过PInvoke调用c++函数的备忘录的实例详解

    C#通过PInvoke调用C++函数的备忘录 什么是PInvoke PInvoke是Platform Invoke的缩写,是.NET Framework提供给C#程序员调用非托管DLL(Dynamic Link Library)在 Windows 平台上的接口技术。PInvoke 提供的主要技术便是 Marshal 类,Marshal 类可以完成 数据类型 …

    C# 2023年6月7日
    00
  • C# 事件的设计与使用深入理解

    C# 事件的设计与使用深入理解 1. 什么是C#事件 在C#中,事件是一种方法,这个方法是用来通知外部程序有某个特定的操作已经发生,以便订阅该事件的程序或方法可以做出相应的响应。事件是C#程序中基于委托的重要机制。 2. C#事件的设计 C#事件的设计包含两个主要部分: 2.1 事件发送者(Publisher) 事件发送者是指负责触发事件的对象,它需要满足以…

    C# 2023年5月31日
    00
  • 深入浅出掌握Unity ShaderLab语法基础

    请听我详细讲解“深入浅出掌握Unity ShaderLab语法基础”的完整攻略。 一、ShaderLab语法基础概述 ShaderLab是Unity中用于编写着色器的语言,它基于CG语言编写,同时又封装了一些常用的函数和数据结构,使得着色器开发变得容易而高效。在使用ShaderLab编写着色器时,需要定义一个合法的Shader程序,并且指定使用哪种渲染方式。…

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