TaskMuxer 2.1.1
dotnet add package TaskMuxer --version 2.1.1
NuGet\Install-Package TaskMuxer -Version 2.1.1
<PackageReference Include="TaskMuxer" Version="2.1.1" />
<PackageVersion Include="TaskMuxer" Version="2.1.1" />
<PackageReference Include="TaskMuxer" />
paket add TaskMuxer --version 2.1.1
#r "nuget: TaskMuxer, 2.1.1"
#:package TaskMuxer@2.1.1
#addin nuget:?package=TaskMuxer&version=2.1.1
#tool nuget:?package=TaskMuxer&version=2.1.1
TaskMuxer
Task Multiplexer.
The purpose of this package is to provide the ability to reduce the amount of parallel repetitive work.
In practical terms, imagine a service that requests data from a slow external API serving multiple requests in parallel.
By using this library, for any number of parallel requests only one will be executed.
Tasks are being identified / segmented by a key and their respective return type.
Compatibility
Current Releases
TaskMuxer Version | .NET Version |
---|---|
2.1.1 | .NET 8, 9 |
2.1.0 | .NET 8, 9 |
2.0.1 | .NET 8 |
2.0.0 | .NET 7, 8 |
Past Releases
Last TaskMuxer Version | .NET Version |
---|---|
1.0.13 | .NET 7 |
Note
There appears to be a compatibility issue for projects targeting .NET 7 and assembly
Assembly Microsoft.Extensions.Hosting, Version=7.0.0.0
and more specifically
OptionsBuilderExtensions.ValidateOnStart
resulting in error:
The call is ambiguous between the following methods or properties: ' Microsoft.Extensions.DependencyInjection.OptionsBuilderExtensions.ValidateOnStart<TOptions>( Microsoft.Extensions.Options.OptionsBuilder<TOptions>)' and ' Microsoft.Extensions.DependencyInjection.OptionsBuilderExtensions.ValidateOnStart<TOptions>( Microsoft.Extensions.Options.OptionsBuilder<TOptions>)'
Until resolved please consider using a previous version of the package targeting .NET 7.
Usage
Registration
Locate the services registration and append one of:
.AddInstanceTaskMultiplexerNoLogger
- use when no logging is required.AddInstanceTaskMultiplexerWithILogger
- use when the ILogger provider is available.AddInstanceTaskMultiplexerWithILoggerFactory
- use when the ILoggerFactory provider is available.AddInstanceTaskMultiplexerWithOptionsAndNoLogger
- use when no logging is required and options are defined.AddInstanceTaskMultiplexerWithOptionsAndILogger
- use when the ILogger provider is available and options are defined.AddInstanceTaskMultiplexerWithOptionsAndILoggerFactory
- use when the ILoggerFactory provider is available and options are defined
ex.
// Startup.cs
public void ConfigureServices(IServiceCollection services)
{
// ...
services
.AddInstanceTaskMultiplexerWithILogger();
// ...
}
📝 If further customization is required, consider wiring up any of the ITaskMultiplexer
implementations as required.
Code
Please check the ITaskMultiplexer
interface for the API surface provided.
ex.
public class ItemsCountService
{
readonly ITaskMultiplexer _taskMultiplexer;
public ItemsCountService(ITaskMultiplexer taskMultiplexer) => _taskMultiplexer = taskMultiplexer;
public async Task<long> GetCountOfItems(CancellationToken cancellationToken = default) =>
await _taskMultiplexer.AddTask(
"items_count",
async ct =>
{
await Task.Delay(250, ct);
return Random.Shared.Next();
},
cancellationToken
);
}
Config
Please consult the InstanceTaskMultiplexerConfig
for all the possible options available.
public record InstanceTaskMultiplexerConfig : IValidatableObject
{
//
public TimeSpan PreserveExecutionResultDuration { get; set; } = TimeSpan.Zero;
public TimeSpan ExecutionTimeout { get; set; } = TimeSpan.FromSeconds(30);
[Range(10, 100_000)]
public int CollectionCapacity { get; set; } = 100;
public virtual IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
if (PreserveExecutionResultDuration < TimeSpan.Zero)
yield return new($"{nameof(PreserveExecutionResultDuration)} must be equal or greater than {TimeSpan.Zero}", new[] { nameof(ExecutionTimeout) });
if (ExecutionTimeout <= TimeSpan.Zero)
yield return new($"{nameof(ExecutionTimeout)} must be greater than {TimeSpan.Zero}", new[] { nameof(ExecutionTimeout) });
}
}
Thread Safety
To achieve thread safety in cases like entity framework core accessing and / or writing data at the same time through parallel threads consider using semaphores or some other form of locking.
ex.
public record SampleEntity
{
public int Id { get; set; }
public string Text { get; set; } = string.Empty;
}
public class SampleDataContext : DbContext
{
public SampleDataContext(DbContextOptions<SampleDataContext> options) : base(options) { }
public DbSet<SampleEntity> Samples { get; set; } = default!;
}
public class SampleService
{
readonly ITaskMultiplexer _taskMultiplexer;
readonly SampleDataContext _dbContext;
private SemaphoreSlim _semaphoreSlim = new(1);
public SampleService(ITaskMultiplexer taskMultiplexer, SampleDataContext dbContext) =>
(_taskMultiplexer, _dbContext) = (taskMultiplexer, dbContext);
public async Task<SampleEntity?> GetById(int id, CancellationToken cancellationToken = default) =>
await _taskMultiplexer.AddTask(
"get_by_id_with_semaphore",
async ct =>
{
await _semaphoreSlim.WaitAsync(ct);
try
{
return await _dbContext.Samples.FirstOrDefaultAsync(x => x.Id == id, ct);
}
finally
{
_semaphoreSlim.Release();
}
},
cancellationToken
);
public async Task<SampleEntity?> Add(SampleEntity obj, CancellationToken cancellationToken = default) =>
await _taskMultiplexer.AddTask(
"add_with_semaphore",
async ct =>
{
await _semaphoreSlim.WaitAsync(ct);
try
{
_dbContext.Add(obj);
await _dbContext.SaveChangesAsync(ct);
}
finally
{
_semaphoreSlim.Release();
}
return obj;
},
cancellationToken
);
}
var service = new SampleService(
new InstanceTaskMultiplexer(),
new SampleDataContext(
new DbContextOptionsBuilder<SampleDataContext>()
.UseInMemoryDatabase("sample_database")
.Options
)
);
await Task.WhenAll(
service.GetById(1),
service.Add(
new()
{
Text = "Random " + Random.Shared.Next()
}
),
service.GetById(1)
);
Similar Projects
Product | Versions Compatible and additional computed target framework versions. |
---|---|
.NET | net8.0 is compatible. 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. net9.0 is compatible. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net8.0
- Microsoft.Extensions.DependencyInjection (>= 9.0.0)
- Microsoft.Extensions.Hosting (>= 9.0.0)
- Microsoft.Extensions.Logging (>= 9.0.0)
- Microsoft.Extensions.Options.ConfigurationExtensions (>= 9.0.0)
- Microsoft.Extensions.Options.DataAnnotations (>= 9.0.0)
-
net9.0
- Microsoft.Extensions.DependencyInjection (>= 9.0.0)
- Microsoft.Extensions.Hosting (>= 9.0.0)
- Microsoft.Extensions.Logging (>= 9.0.0)
- Microsoft.Extensions.Options.ConfigurationExtensions (>= 9.0.0)
- Microsoft.Extensions.Options.DataAnnotations (>= 9.0.0)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
Version | Downloads | Last Updated |
---|---|---|
2.1.1 | 239 | 11/16/2024 |
2.1.0 | 120 | 11/16/2024 |
2.0.1 | 5,171 | 11/22/2023 |
2.0.0 | 129 | 11/21/2023 |
1.0.13 | 1,946 | 9/15/2023 |
1.0.12 | 184 | 9/14/2023 |
1.0.11 | 419 | 9/13/2023 |
1.0.10 | 241 | 9/11/2023 |
1.0.9 | 190 | 9/10/2023 |
1.0.8 | 152 | 9/4/2023 |
1.0.7 | 175 | 9/1/2023 |
1.0.6 | 176 | 9/1/2023 |
1.0.5 | 185 | 8/31/2023 |
1.0.4 | 179 | 8/30/2023 |
1.0.3 | 187 | 8/28/2023 |
1.0.2 | 188 | 8/28/2023 |
1.0.1 | 193 | 8/27/2023 |
1.0.0 | 177 | 8/26/2023 |