.NET 中常用的令牌机制说明

0、目录

.NET 中有多种令牌机制,用于实现不同的同步、取消和通信功能。常见的令牌机制包括

  • CancellationToken: 主要用于取消任务。
  • SemaphoreSemaphoreSlim: 用于控制并发访问的信号量。
  • Mutex: 用于线程或进程间互斥访问。
  • Monitor: 用于线程间同步的锁机制。
  • AutoResetEventManualResetEvent: 用于线程之间的等待和通知。
  • TaskCompletionSource: 用于手动控制异步任务的完成。
  • AccessToken: 用于身份验证和授权的令牌。

每种令牌机制都有其特定的应用场景和功能,可以根据需求选择适合的方式来管理并发、同步和任务控制。

1、具体说明

1.1 CancellationToken

CancellationTokenSource.NET 中用于处理取消操作的一个类,通常与 CancellationToken 一起使用来实现异步操作或长时间运行任务的取消机制。说明:

  • CancellationToken: 这是一个值类型,表示一个可以传递给异步任务、线程或操作的“取消令牌”。通过 CancellationToken,任务能够检测到是否请求了取消操作。
  • CancellationTokenSource: 这是用来创建 CancellationToken 的类,控制取消的来源。当希望取消某个任务时,CancellationTokenSource 负责发出取消信号。

简单来说,CancellationTokenSource 充当了“控制者”的角色,而 CancellationToken 作为“信号”的传递者。

附上案例作以说明:

using System;
using System.Threading;
using System.Threading.Tasks;

class Program
{
    static async Task Main(string[] args)
    {
        // 创建一个 CancellationTokenSource 实例
        var cts = new CancellationTokenSource();

        // 获取 CancellationToken
        CancellationToken token = cts.Token;

        // 启动一个异步任务并传入 CancellationToken
        var task = LongRunningTask(token);

        // 模拟一段时间后请求取消
        await Task.Delay(2000);
        Console.WriteLine("Cancelling the task...");
        cts.Cancel(); // 它会向所有使用该令牌的任务发出取消信号

        // 等待任务完成
        await task;
    }

    static async Task LongRunningTask(CancellationToken token)
    {
        for (int i = 0; i < 10; i++)
        {
            if (token.IsCancellationRequested)
            {
                Console.WriteLine("Task was cancelled.");
                return;
            }
            Console.WriteLine($"Task running: {i + 1}");
            await Task.Delay(1000); // 模拟一个耗时操作
        }
    }
}

CancellationTokenSource 提供了几种方式来控制取消操作的触发

  • 调用 Cancel(): 手动触发取消,通常是在某个条件满足时(如超时、用户请求等)。
  • 超时: 可以为 CancellationTokenSource 设置一个超时,当超时到达时,它会自动发出取消信号。
var cts = new CancellationTokenSource(TimeSpan.FromSeconds(5)); // CancellationTokenSource 会在 5 秒后自动触发取消
  • 链式取消: 可以将多个 CancellationTokenSource 联合在一起。当其中一个源发出取消请求时,所有相关的任务都会被取消。
var cts1 = new CancellationTokenSource();
var cts2 = new CancellationTokenSource();
var linkedCts = CancellationTokenSource.CreateLinkedTokenSource(cts1.Token, cts2.Token);

1.2 SemaphoreSlimSemaphore

这两个类是 .NET 中用于 控制并发访问 的令牌机制。它们用于限制同时访问某个资源的线程数量,通常用于多线程环境中对共享资源的访问进行同步

  • SemaphoreSlim: 是一个轻量级的信号量类,适用于轻量级的线程同步操作。它的主要功能是限制对资源的并发访问,通常比 Semaphore 性能更好,适用于高频次的短时间访问控制。案例代码如下:
var semaphore = new SemaphoreSlim(2);  // 限制最多 2 个线程并发访问

async Task AccessResource()
{
    await semaphore.WaitAsync();  // 获取信号量,等待可用的令牌
    try
    {
        Console.WriteLine("Resource accessed");
        await Task.Delay(1000);  // 模拟访问资源
    }
    finally
    {
        semaphore.Release();  // 释放信号量
    }
}
  • Semaphore: 也是一种信号量,但是它相对来说更重量级,适用于需要在多个进程之间同步的场景(例如跨进程控制共享资源)。案例代码如下:
var semaphore = new Semaphore(2, 5);  // 最小 2 个线程可以访问,最多 5 个线程可以并发

semaphore.WaitOne();  // 获取信号量
try
{
    Console.WriteLine("Resource accessed");
}
finally
{
    semaphore.Release();  // 释放信号量
}

1.3 Mutex(互斥锁)

Mutex 是另一种令牌机制,它用于在多个线程(或进程)之间进行同步,确保在同一时刻只有一个线程可以访问某个资源。与信号量不同,Mutex 主要用于线程间的互斥访问,而不适用于控制并发数量。它可以在进程间使用,因为它是操作系统级别的同步对象。

Mutex 适用于跨线程、跨进程的资源同步。如果线程无法获取 Mutex,它会阻塞等待。

var mutex = new Mutex();

try
{
    mutex.WaitOne();  // 获取互斥锁
    Console.WriteLine("Accessing critical section");
    // 执行需要互斥访问的操作
}
finally
{
    mutex.ReleaseMutex();  // 释放互斥锁
}

1.4 Monitor

Monitor.NET 提供的一种低级同步机制,它为代码块提供互斥锁。它通常与 lock 语句一起使用,是对 Mutex 更轻量的同步方式。Monitor 用于同步线程对共享资源的访问,保证在某个时刻只有一个线程可以进入临界区

Monitor 主要用于线程间的同步,不适用于跨进程的场景。

private static readonly object lockObject = new object();

void AccessSharedResource()
{
    lock (lockObject)  // 这里的 lock 等同于 Monitor.Enter/Monitor.Exit
    {
        Console.WriteLine("Accessing shared resource");
        // 执行需要互斥访问的操作
    }
}

1.5 AutoResetEventManualResetEvent

这两个类是用于线程间通信的同步原语。它们通过信号量机制实现线程的等待和通知机制,通常用于线程之间的协调。

1.5.1 AutoResetEvent

AutoResetEvent 是一种自动重置的事件,其基于“队列”的原理工作的。当多个线程调用 WaitOne() 等待 AutoResetEvent 时,它们会按顺序排队等待信号

当调用 Set() 方法时,它会将事件的状态从无信号(false)切换为信号(true),并立即释放一个等待的线程,释放的是第一个在等待队列中的线程,需注意,此处的释放并不意味着将线程“关闭”或“销毁”,其只是指将线程从等待状态中唤醒,让它继续执行。

此时,释放一个线程后, AutoResetEvent会自动将状态重置为 false,其他线程仍然会处于阻塞状态,直到下次调用 Set()

总结:

  • 每次调用 Set() 会唤醒一个等待中的线程,且 AutoResetEvent会自动将状态重置为 false
  • 唤醒的线程会继续执行,直到它遇到下一个 WaitOne() 时才会再次受 AutoResetEvent 的影响。
  • 唤醒的线程不会因为之前的 Set() 而被干扰,除非它显式地再次调用 WaitOne() 等待。
var autoResetEvent = new AutoResetEvent(false);  // 初始状态为无信号

// 线程1:发送信号
autoResetEvent.Set();

// 线程2:等待信号
autoResetEvent.WaitOne();  // 会阻塞,直到收到信号

1.5.2 ManualResetEvent

ManualResetEvent 有一个 信号状态,它可以是 truefalse。如果状态为 true,那么所有等待的线程都会被唤醒并继续执行;如果状态为 false,那么所有调用 WaitOne() 的线程都会被阻塞,直到状态变为 true

AutoResetEvent相比,ManualResetEvent是一种手动重置的事件,它不会自动重置。调用 Set 后,所有等待的线程都会被通知并继续执行,直到手动调用 Reset 来将状态重置为无信号。

关键点:

  • Set() 方法:当调用 Set() 时,ManualResetEvent 的状态会被设置为 true。此时,所有 在 WaitOne() 上等待的线程都会被唤醒并继续执行。(调用WaitOne()会使线程进入等待队列)
    这个唤醒过程是批量的,与 AutoResetEvent 不同,后者每次只释放一个等待的线程。ManualResetEvent 会释放所有等待的线程。

  • Reset() 方法:当调用 Reset() 时,ManualResetEvent 会将状态设置回 false,使得所有后续调用 WaitOne() 的线程都会被阻塞,直到下次调用 Set()

  • 状态持久性:与 AutoResetEvent 不同,ManualResetEvent 的状态在被设置为 true 后,会持续保持为 true,直到显式调用 Reset() 将其重置为 false。因此,ManualResetEvent 可以让多个线程同时继续执行,直到手动调用 Reset() 来阻塞它们。

总结:

  • 调用 Set():状态从 false 变为 true,所有等待的线程都会被唤醒,并继续执行各自的任务。此时,ManualResetEvent 保持为 true,并且所有后续的 WaitOne() 调用将不会阻塞,直到状态被重置。

  • 调用 Reset():将状态从 true 设置为 false,此时 所有新的 WaitOne() 调用 会被阻塞,直到下一次调用 Set()

案例代码如下:

ManualResetEvent mre = new ManualResetEvent(false);  // 初始状态为 false
Thread t1 = new Thread(() =>
{
    Console.WriteLine("Thread 1 waiting...");
    mre.WaitOne();  // 线程 1 会阻塞,直到状态为 true
    Console.WriteLine("Thread 1 started.");
});
Thread t2 = new Thread(() =>
{
    Console.WriteLine("Thread 2 waiting...");
    mre.WaitOne();  // 线程 2 会阻塞,直到状态为 true
    Console.WriteLine("Thread 2 started.");
});
t1.Start();
t2.Start();

Thread.Sleep(1000);  // 模拟一些延时

Console.WriteLine("Main thread setting event.");
mre.Set();  // 触发 Set(),将状态设为 true,唤醒所有等待的线程

// 输出:
// Thread 1 waiting...
// Thread 2 waiting...
// Main thread setting event.
// Thread 1 started.
// Thread 2 started.

1.6 TaskCompletionSource

虽然 TaskCompletionSource 本身不算一个典型的令牌,但它允许创建自定义的异步任务控制机制。它是一个用于手动控制异步任务完成状态的对象,通常与异步编程结合使用。它提供了一个 Task,可以通过设置任务的完成状态来控制任务何时完成。

TaskCompletionSource 通常用于自定义的异步操作中,可以在操作完成时标记任务为已完成、已取消或已失败。

var tcs = new TaskCompletionSource<bool>();

// 模拟一些操作
Task.Run(() =>
{
    // 进行一些耗时操作
    Thread.Sleep(2000);
    tcs.SetResult(true);  // 操作完成
});

// 等待结果
bool result = await tcs.Task;
Console.WriteLine($"Operation result: {result}");

1.7 AccessToken(授权令牌)

在一些需要身份验证和授权的应用中,AccessToken 是一种用于控制用户访问权限的令牌。它通常用于基于 OAuth 或其他身份验证协议的应用程序中,确保用户能够访问特定的资源。

AccessToken 是用来标识和验证用户身份的令牌,通常由身份提供者(如 OAuth2.0 提供者)颁发,并且可以在 API 请求中携带。

var accessToken = "your-access-token";
var client = new HttpClient();
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);

var response = await client.GetAsync("https://api.example.com/data");
var data = await response.Content.ReadAsStringAsync();

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值