License 授权机制构思
在 .NET
中实现 License
授权机制,可以通过以下步骤进行:
1.确定授权方式
- 基于时间的授权:限制软件的使用时间。
- 基于功能的授权:根据用户购买的版本,开放不同的功能。
- 基于并发数的授权:限制同时使用软件的用户或设备数量。
2.生成许可证文件
- 包含信息:用户信息、授权类型、有效期、签名等。
- 使用加密算法:采用非对称加密算法(如RSA)对许可证文件进行签名,防止改。
3.验证许可证
- 解析许可证文件:读取并解析许可证中的信息。
- 验证签名:使用公钥验证许可证的签名,确保其合法性。
- 检查授权条件:根据许可证中的信息,检查授权是否有效。
4.集成到应用程序
- 启动时验证:在应用程序启动时验证许可证,如果无效则提示用户或限制功能。
- 提供管理界面:允许用户输入许可证密钥或查看授权信息。
注意事项
- 安全性:确保许可证文件的安全性,防止被非法获取或算改。
- 用户体验:在授权验证失败时,提供友好的提示信息,避免影响用户体验。
- 灵活性:根据实际需求,设计灵活的授权机制,方便后续扩展和维护。
通过以上步骤,可以在 .NET
应用程序中实现一个基本的 License
授权机制,确保软件的合法使用。
继续完善授权机制
为了完善 .NET
中的 License
授权机制,使其包含时间效期、应用功能和并发数限制等,您可以按照以下步骤进行:
说明:实际业务场景请具体问题考虑,这里只是抛砖引玉。
1.许可证文件结构
设计一个包含以下信息的许可证文件格式:
- 时间效期:许可证的有效开始日期和结束日期。
- 应用功能:列出可用的功能模块或权限级别。
- 并发数限制:允许同时使用的最大用户数或设备数。
2.生成许可证文件
在生成许可证文件时,将这些信息嵌入到文件中,并使用加密算法进行签名。
3.验证许可证
在验证许可证时,除了检查签名和时间效期外,还需要检查功能和并发数限制:
- 功能验证:根据许可证中的功能列表,检查用户是否有权限使用特定功能。
- 并发数验证:维护一个计数器或记录,跟踪当前使用的用户或设备数量,确保不超过许可证允许的并发数。
4.集成到应用程序
- 启动时验证:在应用程序启动时,验证许可证的时间效期和并发数限制。
- 功能访问控制:在用户尝试访问特定功能时,验证其是否在许可证允许的功能列表中。
- 并发数管理:在用户登录或启动新实例时,检查并发数限制,并在用户注销或实例关闭时更新计数器。
注意事项
- 同步和并发管理:确保在多用户或多线程环境中,并发数的计数和管理是同步和准确的。
- 动态更新:考虑实现许可证的动态更新机制,允许在不影响用户使用的情况下更新许可证信。
- 息日志记录:记录许可证验证和使用的日志,便于追踪和审计。
通过以上步骤,您可以实现一个包含时间效期、应用功能和并发数限制的完整 License
授权机制。
授权证书文件格式介绍
授权证书生成文件的后缀通常取决于其编码格式和用途。
以下是一些常见的授权证书文件后缀:
- .pem:
Privacy Enhanced Mail
的缩写,采用Base64
编码的ASCIl
文本格式,可包含证书.私钥或两者皆有。 - .der:
Distinguished Encoding Rules
的缩写,二进制编码格式,常用于存储X.509
证书。 - .crt:
Certificate
的缩写,常用于存储公钥证书,格式可以是PEM
或DER
。 - .cer:与
.crt
类似,用于存储证书,编码格式通常为DER
。 - .pfx:
Personal Information Exchange
的缩写,包含证书和私钥的二进制格式,常用于Windows
平台,可通过密码保护。 - .p12:与
.pfx
相同,是PKCS #12
标准的另一种扩展名。 - .jks:
Java KeyStore
的缩写,用于存储证书和私钥,常用于Java
平台。 - .lic:
License
的缩写,特定软件或系统的授权证书文件,格式和内容自定义。
这些后缀代表了不同的证书格式和编码方式,在实际应用中,应根据具体需求和场景选择合适的文件格式。
BouncyCastle.Cryptography 包介绍
一个开源的密码学库,提供广泛的加密算法和协议实现,支持 .NET
平台。
核心功能
1.算法支持
- 对称加密(如
AES、DES
) - 非对称加密(如
RSA、SM2
) - 数字签名(如
ECDSA、SM3WithSM2
) - 哈希算法(如
SHA-256、SM3
) 国密算法(如
SM2、SM3、SM4
)2.证书与密钥管理
X.509
证书解析/生成- 密钥对(
AsymmetricCipherKeyPair
)生成与存储 PKCS#1/PKCS#8
密钥格式支持3.协议支持
TLS/SSL
相关功能CMS
(加密消息语法)4.适用场景
需要兼容国密算法(如
SM2/SM3/SM4
)的项目- 自定义证书签名/验证流程(如您代码中的
License
签名) - 低层级密码学操作(如手动处理密钥与加密流程)
在项目中安装使用:
# 通过 NuGet 安装
Install-Package BouncyCastle.Cryptography
注意事项
- 版本兼容性:确保选择与目标
.NET
版本兼容的包版本(当前最新为2.2.1+
)。 - 性能:相比
.NET
内置加密库(如System.Security.Cryptography
),某些场景可能性能较低。 - 文档:官方文档较少,建议参考 源码示例。
为啥选择 SM2 国密算法?
在选择签名算法时,此处本人采用 SM2
国密算法,原因如下:
安全性
- 基于椭圆曲线密码学:
SM2
算法的安全性基于椭圆曲线离散对数问题,具有较高的安全强度。256
位的SM2
密码强度已经比2048
位的RSA
密码强度要高。 - 国家密码管理局认可:
SM2
算法是中国国家密码管理局批准的公钥密码算法,经过严格的审查和测试,符合国家安全标准。
效率
- 签名速度快:相较于
RSA
算法,SM2
算法在签名和密钥生成速度上更快,特别是在处理大量数据时,效率优势明显。 - 密钥长度短:
SM2
算法使用较短的密钥长度(256
位),减少了存储和传输的开销。
适用性
- 广泛应用:
SM2
算法适用于数字签名、密钥交换和数据加密等多种场景,满足多种密码应用的安全需求。 - 支持国密标准:
SM2
算法与SM3
哈希算法结合使用,符合中国国密标准,适用于需要遵循国内安全标准的系统和应用。
兼容性
- 开源支持:许多开源密码学库(如
BouncyCastle
)已经支持SM2
算法,便于集成和开发。 - 硬件支持:国内许多加密芯片和硬件设备支持
SM2
算法,提供硬件级的加速和安全保障。
综上所述,SM2
算法在安全性、效率和适用性方面都表现出色,是签名国密算法的理想选择。
基于 SM2 国密算法如何使用 .net 实现 License 证书?
为了方便演示,本人此处使用新建 .net9
控制台应用程序,并添加依赖包:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net9.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="BouncyCastle.Cryptography" Version="2.5.1" />
</ItemGroup>
</Project>
项目(LicenseDemo
)结构如下所示:
为了使用 SM2
算法完善签名并生成 .lic
授权证书,您可以按照以下步骤进行:
1. 生成 SM2 密钥对
首先,您需要生成 SM2
算法的公钥和私钥。可以使用 C#
中的BouncyCastle.Cryptography
库来实现。
在 C#
中,ECKeyGenerationParameters
类用于生成椭圆曲线密钥对时指定参数。选择椭圆曲线参数集,这里使用的是 "sm2p256v1"
,然后创建一个安全的随机数生成器 SecureRandom()
,接着实例化 ECKeyGenerationParameters
。
创建 ECKeyPairGenerator
实例,初始化密钥生成器,然后调用 GenerateKeyPair()
方法,即可生成 SM2
算法的密钥对。
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Generators;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Asn1.X9;
using Org.BouncyCastle.Security;
using Org.BouncyCastle.Asn1.GM;
using System.Text;
namespace LicenseDemo.Licenses;
/// <summary>
/// 生成SM2密钥对
/// </summary>
class Sm2KeyPairGenerator
{
#region 单例模式
// 使用 Lazy<T> 实现懒加载
private static readonly Lazy<Sm2KeyPairGenerator> _instance = new(() => new Sm2KeyPairGenerator());
// 私有构造函数,防止外部实例化
private Sm2KeyPairGenerator() {
}
// 公共静态属性,用于访问单例实例
public static Sm2KeyPairGenerator Instance => _instance.Value;
#endregion
#region 密钥对生成算法
// 使用【sm2p256v1】曲线,获取SM2曲线的X9ECParameters对象
private static readonly X9ECParameters x9ECParameters = GMNamedCurves.GetByName("sm2p256v1");
// 使用X9ECParameters对象创建ECDomainParameters对象
private static readonly ECDomainParameters domainParams = new(
x9ECParameters.Curve,
x9ECParameters.G,
x9ECParameters.N,
x9ECParameters.H,
x9ECParameters.GetSeed()
);
private static readonly ECKeyPairGenerator generator = new();
/// <summary>
/// 生成SM2密钥对(公钥,私钥)
/// </summary>
/// <returns></returns>
public AsymmetricCipherKeyPair GenerateKeyPair()
{
generator.Init(new ECKeyGenerationParameters(domainParams, new SecureRandom()));
return generator.GenerateKeyPair();
}
#endregion
/// <summary>
/// 测试,输出SM2密钥对信息
/// </summary>
public void KeyPairInfo()
{
StringBuilder strBuilder = new(128);
strBuilder.AppendLine("Hello, SM2KeyPairGenerator!");
// 输出曲线名称
strBuilder.AppendLine("Curve Name: sm2p256v1");
// 输出曲线参数
strBuilder.AppendLine($"Curve: {x9ECParameters.Curve}");
strBuilder.AppendLine($"G:{x9ECParameters.G}");
strBuilder.AppendLine($"N:{x9ECParameters.N}");
strBuilder.AppendLine($"H:{x9ECParameters.H}");
byte[]? seed = x9ECParameters.GetSeed();
if (seed != null)
{
strBuilder.AppendLine($"Seed:{BitConverter.ToString(seed)}");
}
// 输出ECDomainParameters对象
strBuilder.AppendLine($"ECDomainParameters: {domainParams}");
Console.WriteLine(strBuilder.ToString());
}
}
2. 生成 SM2 签名/验签 & 加密/解密
- 签名 & 验签
在生成密钥对后,您可以使用私钥对数据进行签名,
// 使用SM2私钥对数据进行签名
public byte[] SignData(byte[] data, AsymmetricKeyParameter privateKey)
{
var signer = SignerUtilities.GetSigner("SM3WITHSM2");
signer.Init(true, privateKey);
signer.BlockUpdate(data, 0, data.Length);
return signer.GenerateSignature();
}
验证签名时,使用对应的公钥:
// 使用SM2公钥对数据进行验签
public bool VerifyData(byte[] data, byte[] signature, AsymmetricKeyParameter publicKey)
{
var verifier = SignerUtilities.GetSigner("SM3WITHSM2");
verifier.Init(false, publicKey);
verifier.BlockUpdate(data, 0, data.Length);
return verifier.VerifySignature(signature);
}
- 加密 & 解密
除了上面的签名和验签之外,我们还可以利用公钥加密,私钥解密。
// 使用SM2公钥加密
public string Encrypt(string plainText, AsymmetricKeyParameter publicKey)
{
SM2Engine engine = new();
engine.Init(true, new ParametersWithRandom(publicKey, new SecureRandom()));
byte[] plainTextBytes = Encoding.UTF8.GetBytes(plainText);
byte[] encryptedBytes = engine.ProcessBlock(plainTextBytes, 0, plainTextBytes.Length);
return Convert.ToBase64String(encryptedBytes);
}
// 使用SM2私钥解密
public string Decrypt(string encryptedText, AsymmetricKeyParameter privateKey)
{
SM2Engine engine = new();
engine.Init(false, privateKey);
byte[] encryptedBytes = Convert.FromBase64String(encryptedText);
byte[] decryptedBytes = engine.ProcessBlock(encryptedBytes, 0, encryptedBytes.Length);
return Encoding.UTF8.GetString(decryptedBytes);
}
3. 定义 License 数据结构
License
数据结构是针对应用授权的定义,根据自己实际业务情况,可以自定义扩展定义。
/// <summary>
/// License 数据
/// </summary>
class LicenseData
{
public string Id => Guid.NewGuid().ToString();
public string AppName {
get; set; } = null!;
public string AppVersion {
get; set; } = null!;
public string AuthCode {
get; set; } = null!;
public string UserName {
get; set; } = null!;
public string CompanyName {
get; set; } = null!;
public DateTimeOffset StartDate {
get; set; }
public DateTimeOffset ExpirationDate {
get; set; }
public List<string> AllowedFeatures {
get; set; } = [];
}
4. 定义 License 上下文对象
上下文对象用于承载证书数据的形态,但为了安全性保障,该上下文对象会被再次加密,然后将密文写入 .lic
文件中。
/// <summary>
/// License 上下文
/// </summary>
internal class LicenseContent
{
/// <summary>
/// 数据
/// </summary>
public string Data {
get; set; } = null!;
/// <summary>
/// 签名
/// </summary>
public string Signature {
get; set; } = null!;
}
上面基本信息铺垫后,接下来我们开始实现 License
证书的生成与校验。
5. 实现 License 授权助手
- 在生成
.lic
授权证书时,您可以将签名数据和其他相关信息(如用户信息、授权类型、时间限制等)一起包含在证书中; - 在生成
.lic
授权证书时,您需要提取数据和签名,然后使用公钥进行验证;
using Org.BouncyCastle.Crypto;
using System.Text.Json;
using System.Text;
namespace LicenseDemo.Licenses;
/// <summary>
/// license 授权助手
/// </summary>
/// <param name="publicKey">公钥</param>
/// <param name="privateKey">私钥</param>
class LicenseAssistant(AsymmetricCipherKeyPair keyPair)
{
private readonly Sm2Assistant _sm2Assistant = new();
private AsymmetricKeyParameter PublicKey => keyPair.Public;
private AsymmetricKeyParameter PrivateKey => keyPair.Private;
/// <summary>
/// 生成License文件(.lic授权证书)
/// </summary>
/// <param name="licenseData"></param>
/// <param name="outputFile"></param>
/// <returns></returns>
public async Task GenerateLicenseFileAsync(LicenseData licenseData, string outputFile)
{
// 序列化数据
string jsonString = await JsonSerializeAsync(licenseData);
// 对数据进行签名
byte[] dataToSign = Encoding.UTF8.GetBytes(jsonString);
byte[] signature = _sm2Assistant.SignData(dataToSign, PrivateKey);
// 组合数据和签名
LicenseContent licenseContent = new()
{
Data = jsonString,
Signature = Convert.ToBase64String(signature)
};
// 异步序列化 JSON 数据
string license = await JsonSerializeAsync(licenseContent);
// 公钥加密
string licenseCiphertext = _sm2Assistant.Encrypt(license, PublicKey);
// 写入文件
await File.WriteAllTextAsync(outputFile, licenseCiphertext);
}
/// <summary>
/// 验证License文件(.lic授权证书)
/// </summary>
/// <param name="licenseFilePath"></param>
/// <returns></returns>
public async Task<bool> VerifyLicenseFileAsync(string licenseFilePath)
{
// 读取文件内容
string licenseCiphertext = await File.ReadAllTextAsync(licenseFilePath);
// 私钥解密
string licenseContent = _sm2Assistant.Decrypt(licenseCiphertext, PrivateKey);
// 异步反序列化 JSON 数据
var licenseData = await JsonDeserializeAsync<LicenseContent>(licenseContent);
// 解析数据和签名
string data = licenseData.Data;
byte[] signature = Convert.FromBase64String(licenseData.Signature);
// 验证签名
byte[] verify = Encoding.UTF8.GetBytes(data);
return _sm2Assistant.VerifyData(verify, signature, PublicKey);
}
/// <summary>
/// 序列化数据
/// </summary>
/// <typeparam name="TData"></typeparam>
/// <param name="data"></param>
/// <returns></returns>
private async Task<string> JsonSerializeAsync<TData>(TData data)
{
using var stream = new MemoryStream();
await JsonSerializer.SerializeAsync(stream, data);
stream.Position = 0;
using var reader = new StreamReader(stream);
return await reader.ReadToEndAsync();
}
/// <summary>
/// 反序列化数据
/// </summary>
/// <typeparam name="TData"></typeparam>
/// <param name="data"></param>
/// <returns></returns>
private async Task<TData> JsonDeserializeAsync<TData>(string data)
{
if (string.IsNullOrWhiteSpace(data))
return default!;
// 解析数据和签名
byte[] jsonBytes = Encoding.UTF8.GetBytes(data);
using var stream = new MemoryStream(jsonBytes);
// 将 JSON 字符串写入内存流
//await stream.WriteAsync(jsonBytes, 0, jsonBytes.Length);
//stream.Position = 0;
// 异步反序列化 JSON 数据
return await JsonSerializer.DeserializeAsync<TData>(stream);
}
}
应用程序中如何使用?
在 Program.cs
文件中添加如下代码:
// See https://aka.ms/new-console-template for more information
using LicenseDemo.Licenses;
using System.Diagnostics;
using System.Text;
{
// 1.生成SM2密钥对
var sm2KeyPairGenerator = Sm2KeyPairGenerator.Instance;
sm2KeyPairGenerator.KeyPairInfo();
var keyPair = sm2KeyPairGenerator.GenerateKeyPair();
Console.WriteLine($"public => {keyPair.Public}");
Console.WriteLine($"private => {keyPair.Private}");
// 2.使用SM2签名数据 & 验证签名
Sm2Assistant sm2Assistant = new();
var data = Encoding.UTF8.GetBytes("hello");
var signature = sm2Assistant.SignData(data, keyPair.Private);
Console.WriteLine($"sign => {signature}");
var verify = sm2Assistant.VerifyData(data, signature, keyPair.Public);
Console.WriteLine($"verify => {verify}");
// 3.创建许可证数据
var licenseData = new LicenseData
{
AppName = "MyApp",
AppVersion = "1.0.0",
AuthCode = "1234567890",
UserName = "John Doe",
CompanyName = "ACME Corp",
StartDate = DateTime.UtcNow,
ExpirationDate = DateTime.UtcNow.AddDays(30),
AllowedFeatures = ["PremiumFeature1", "PremiumFeature2"]
};
// 4. 实例化 License 助手
LicenseAssistant licenseAssistant = new(keyPair);
// 4.1 设置 .lic 证书存放路径
string targetDirectory = "lics";
Directory.CreateDirectory(targetDirectory);
Console.WriteLine($"Project Folder Path: {targetDirectory}");
string outputFilePath = Path.Combine(targetDirectory, "license.lic");
// 4.2 生成许可证文件
await licenseAssistant.GenerateLicenseFileAsync(licenseData, outputFilePath);
// 4.3 验证许可证文件
bool isValid = await licenseAssistant.VerifyLicenseFileAsync(outputFilePath);
Console.WriteLine($"License is valid: {isValid}");
}
注意事项
- 密钥管理:确保私钥的安全存储,避免泄露。
- 安全性:考虑使用其他安全措施,如加密数据、添加时间戳等,增强证书的安全性。
- 可读性:虽然加密和签名增加了安全性,但也要确保生成的。
通过上述步骤,您可以使用 SM2
算法完善签名并生成 .lic
授权证书,确保数据和签名的安全性和完整性。
生成 License 证书性能测试
为了更好的利用计算机资源,此处使用异步多线程方式生成多个 .lic
文件。
说明:性能测试基准,生成
10000
个许可证文件,记录生成耗时。
方法一:使用 Parallel.For
这里使用了 async
关键字,但 Parallel.For
是为同步代码设计的。虽然它支持 async
,但可能会导致一些潜在的问题,比如任务调度不当或资源浪费。
int licenseCount = 10000; // 指定 10000 个许可证文件
{
Stopwatch stopwatch = Stopwatch.StartNew();
await Task.Run(() =>
{
Parallel.For(0, licenseCount, async i =>
{
LicenseData licenseData = new()
{
AppName = "MyApp",
AppVersion = "1.0.0",
AuthCode = "123456",
UserName = "John Doe",
CompanyName = "Acme Inc.",
StartDate = DateTimeOffset.Now,
ExpirationDate = DateTimeOffset.Now.AddDays(365),
AllowedFeatures = ["Feature1", "Feature2"]
};
string licenseFile = Path.Combine(targetDirectory, $"license1-{i}.lic");
await licenseAssistant.GenerateLicenseFileAsync(licenseData, licenseFile);
});
});
stopwatch.Stop();
Console.WriteLine($"方法 1:使用 Parallel.For,Generated {licenseCount} license files in {stopwatch.ElapsedMilliseconds} ms");
}
方法 2:使用 Task.WhenAll
这种方式更符合现代化 .net
异步编程的最佳实践,能够更好地利用 I/O
并发的优势,避免阻塞主线程。
{
Stopwatch stopwatch2 = Stopwatch.StartNew();
List<Task> tasks = [];
for (int i = 0; i < licenseCount; i++)
{
int index = i; // 捕获循环变量
tasks.Add(Task.Run(async () =>
{
LicenseData licenseData = new()
{
AppName = "MyApp",
AppVersion = "1.0.0",
AuthCode = "123456",
UserName = "John Doe",
CompanyName = "Acme Inc.",
StartDate = DateTimeOffset.Now,
ExpirationDate = DateTimeOffset.Now.AddDays(365),
AllowedFeatures = ["Feature1", "Feature2"]
};
string licenseFile = Path.Combine(targetDirectory, $"license2-{index}.lic");
await licenseAssistant.GenerateLicenseFileAsync(licenseData, licenseFile);
}));
}
await Task.WhenAll(tasks);
stopwatch2.Stop();
Console.WriteLine($"方法 2:使用 Task.WhenAll,Generated {licenseCount} license files in {stopwatch2.ElapsedMilliseconds} ms");
}
本人测试电脑配置比较差,信息如下:
- 执行命令:
dotnet run
- 输出信息:
Hello, SM2KeyPairGenerator!
Curve Name: sm2p256v1
Curve: Org.BouncyCastle.Math.EC.FpCurve
G:(32c4ae2c1f1981195f9904466a39c9948fe30bbff2660be1715a4589334c74c7,bc3736a2f4f6779c59bdcee36b692153d0a9877cc62a474002df32e52139f0a0,1,fffffffefffffffG:(32c4ae2c1f1981195f9904466a39c9948fe30bbff2660be1715a4589334c74c7,bc3736a2f4f6779c59bdcee36b692153d0a9877cc62a474002df32e52139f0a0,1,fffffffeffffffG:(32c4ae2c1f1981195f9904466a39c9948fe30bbff2660be1715a4589334c74c7,bc3736a2f4f6779c59bdcee36b692153d0a9877cc62a474002df32e52139f0a0,1,fffffffeffffffG:(32c4ae2c1f1981195f9904466a39c9948fe30bbff2660be1715a4589334c74c7,bc3736a2f4f6779c59bdcee36b692153d0a9877cc62a474002df32e52139f0a0,1,fffffffeffffffffffffG:(32c4ae2c1f1981195f9904466a39c9948fe30bbff2660be1715a4589334c74c7,bc3736a2f4f6779c59bdcee36b692153d0a9877cc62a474002df32e52139f0a0,1,fffffffeffffG:(32c4ae2c1f1981195f9904466a39c9948fe30bbff2660be1715a4589334c74c7,bc3736a2f4f6779c59bdcee36b692153d0a9877cc62a474002df32e52139f0a0,1,fffffffeffffG:(32c4ae2c1f1981195f9904466a39c9948fe30bbff2660be1715a4589334c74c7,bc3736a2f4f6779c59bdcee36b692153d0a9877cc62a474002df32e52139f0a0,1,fffffffeffff
ffffffffffffffffffffffffffff00000000fffffffffffffffc)
N:115792089210356248756420345214020892766061623724957744567843809356293439045923
H:1
ECDomainParameters: Org.BouncyCastle.Crypto.Parameters.ECDomainParameters
public => Org.BouncyCastle.Crypto.Parameters.ECPublicKeyParameters
private => Org.BouncyCastle.Crypto.Parameters.ECPrivateKeyParameters
sign => System.Byte[]
verify => True
Project Folder Path: lics
License is valid: True
方法 1:使用 Parallel.For,Generated 10000 license files in 81969 ms
方法 2:使用 Task.WhenAll,Generated 10000 license files in 73862 ms
说明,依据电脑配置情况,生成文件耗时会有差异,此处大概参考。
电脑配置规格:cpu:intel i7-12650h
,内存:16g ddr4
,硬盘:1 t ssd
生成10000
个.lic
文件,大概耗时18-19
秒左右。
感兴趣的下伙伴请自行测试哟。
License
证书文件如下:
关于 Parallel.For & Task.WhenAll 的对比
1.工作原理
Parallel.For
- 使用 TPL (Task Parallel Library) 来并行执行循环体。
- 内部管理线程池,自动分配任务到多个线程。
- 更适合 CPU 密集型任务。
Task.WhenAll
- 创建多个异步任务,并等待所有任务完成。
- 每个任务可以独立运行,适用于 I/O 密集型任务(如文件操作、网络请求)。
- 提供更好的控制和灵活性,可以组合不同的异步操作。
2.适用场景
Parallel.For
- 适用于需要并行处理大量数据的场景。
- 例如:计算密集型任务,如矩阵运算、图像处理等。
Task.WhenAll
- 适用于需要并发执行多个异步操作的场景。
- 例如:并发读写文件、发起多个 HTTP 请求等。
3.性能对比
Parallel.For
对于 CPU 密集型任务,性能较好。
对于 I/O 密集型任务,可能因为线程切换频繁而降低性能。
Task.WhenAll
对于 I/O 密集型任务,性能更好,因为它不会阻塞线程,而是让线程去执行其他任务。
在你的例子中,生成许可证文件是 I/O 密集型任务,因此 Task.WhenAll 更合适。
4.结论
如果你需要处理 I/O 密集型任务(如文件操作、网络请求),建议使用 Task.WhenAll。
如果你需要处理 CPU 密集型任务(如大量计算),可以考虑使用 Parallel.For。
在上述的代码中,生成 .lic
许可证文件属于 I/O
密集型任务,因此推荐使用 Task.WhenAll
方式来提高性能和稳定性。