MindLab.Threading 1.0.2

dotnet add package MindLab.Threading --version 1.0.2
NuGet\Install-Package MindLab.Threading -Version 1.0.2
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="MindLab.Threading" Version="1.0.2" />
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add MindLab.Threading --version 1.0.2
#r "nuget: MindLab.Threading, 1.0.2"
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
// Install MindLab.Threading as a Cake Addin
#addin nuget:?package=MindLab.Threading&version=1.0.2

// Install MindLab.Threading as a Cake Tool
#tool nuget:?package=MindLab.Threading&version=1.0.2

MindLab.Threading

MindLab.Threading主要封装了一些在面向async/await异步编程时所使用的线程间同步对象。

使用

用户可从nuget中下载: MindLab.Threading

示例

IAsyncLock接口

IAsyncLock表示一个支持异步操作的互斥锁对象接口,目前的实现类有三种,分别是MonitorLockCasLock以及SemaphoreLock。下面的样例代码演示实现线程安全的自增/自减类。

PS:这里只是为了演示需要,在实际的项目中,我们建议使用原子操作(Interlocked)实现线程安全的自增功能

public class MyClass
{
    // 此处可以替换为 new CasLock() 或 new SemaphoreLock()
    private readonly IAsyncLock m_lock = new MonitorLock();
    private int m_value;    
    
    public async Task IncreaseAsync(CancellationToken cancellation)
    {
        await using(await m_lock.LockAsync(cancellation))
        {
          m_value++;
        }
    }
     
    public async Task<bool> TryIncrease()
    {
        if(!m_lock.TryLock(out var locker))
        {
            return false;
        }
        m_value++;
        await locker.DisposeAsync();
        return true;
    }
    
    public async Task DecreaseAsync(CancellationToken cancellation)
    {
        await using(await m_lock.LockAsync(cancellation))
        {
            m_value--;
        }
    }
}

上述代码中,无论是使用MonitorLockCasLock或者SemaphoreLock,其功能都是一致的,只是底层的实现方式不同,所以性能上不一样。

  • MonitorLock :内部基于System.Threading.Monitor实现,从目前的测试数据来看,性能最好;

  • CasLock : 内部基于原子操作的Lock-Free实现,不会导致调用线程的挂起堵塞;

  • SemaphoreLock:内部基于System.Threading.SemaphoreSlim实现异步处理;

OnceFlag

在我们日常的应用场景中,尤其是在实现Dispose方法的时候,常常需要编写这种形式的代码:

private bool m_disposed; // 指示当前对象是否已经被disposed
private readonly object m_disposeLock = new object();

public void Dispose()
{
    if(m_disposed)
    {
        return;
    }
    
    lock(m_disposeLock)
    {
        if(m_disposed)
        {
            return;
        }
        
        m_disposed = true;
    }
    
    //TODO: 释放资源 ...
}

上述代码是为了避免调用方会多线程地调用Dispose()方法,这种写法较为麻烦,所以我们提供了OnceFlag来处理类似的需求:

private readonly OnceFlag m_disposeFlag = new OnceFlag();

public void Dispose()
{
    if(!m_disposeFlag.TrySet())
    {
        return;
    }
    //TODO: 释放资源 ...
}

OnceFlag内部采用Lock-Free实现,相比起直接用lock会更加高效。

AsyncBlockingCollection

AsyncBlockingCollection 在功能上等价于原生的 System.Collections.Concurrent.BlockingCollection, 不同之处在于完全使用了基于async/await的异步接口。以下示例模拟了从文件中加载文本并异步输出到控制台的功能:

private readonly AsyncBlockingCollection<string> m_queue = new AsyncBlockingCollection<string>(capacity:128);

public async Task RunAsync(string fileName, CancellationToken token)
{
    var readTask = LoadDataAsync(fileName, token);
    var writeTask = ShowDataAsync(token);
    Task.WhenAll(readTask, writeTask);
}

private async Task LoadDataAsync(string fileName, CancellationToken token)
{
    await using var fs = File.OpenRead(fileName);
    using var reader = new StreamReader(fs);
    
    while(!token.IsCancellationRequested)
    {
        var lineText = await reader.ReadLineAsync();
        await m_queue.AddAsync(lineText, token);
    }
}

private async Task ShowDataAsync(CancellationToken token)
{
    while(!token.IsCancellationRequested)
    {
        var lineText = await m_queue.TakeAsync(token);
        Console.WriteLine(lineText);
    }
}

AsyncReaderWriterLock

AsyncReaderWriterLock 在功能上与 System.Threading.ReaderWriterLockSlim 类似, 只是采用了异步方法, 但我们没有提供 EnterUpgradableReadLock 的功能。

class MyClass
{
    private readonly List<int> m_values = new List<int>();
    private readonly AsyncReaderWriterLock m_lock = new AsyncReaderWriterLock();
    
    public async Task<bool> ContainsAsync(int value, CancellationToken cancellation)
    {
        using(await m_lock.WaitForReadAsync(cancellation))
        {
            return m_values.Contains(value);
        }
    }
    
    public async Task AddAsync(int value, CancellationToken cancellation)
    {
        using(await m_lock.WaitForWriteAsync(cancellation))
        {
            m_values.Add(value);
        }
    }
}
Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 was computed.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 was computed.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed. 
.NET Core netcoreapp2.0 was computed.  netcoreapp2.1 was computed.  netcoreapp2.2 was computed.  netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.0 is compatible.  netstandard2.1 is compatible. 
.NET Framework net461 was computed.  net462 was computed.  net463 was computed.  net47 was computed.  net471 was computed.  net472 was computed.  net48 was computed.  net481 was computed. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen40 was computed.  tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (1)

Showing the top 1 NuGet packages that depend on MindLab.Threading:

Package Downloads
MindLab.Messaging

MindLab.Messaging 提供了一个轻量级的消息订阅/发布模式(进程内),支持消息单播、广播,所有接口均为使用async/await形式的Task异步接口。

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last updated
1.0.2 1,407 3/22/2020
1.0.0 546 3/18/2020