Skip to content

Ryujose/NetTaskManagement

Repository files navigation

NetTaskManagement

CI codecov

A .NET Standard 2.0 library for Task Parallel Library (TPL) orchestration with Dependency Injection support.

Tasks are identified by string key and managed through a status-based API — no raw Task objects exposed to callers. The library handles the full task lifecycle: register → start → cancel → check completion → delete, and exposes an append-only observability queue that captures disposal metadata for external monitoring.

NetTaskManagement vs TPL Dataflow — which one do you need?

These are complementary tools, not competing ones. The benchmarks below are meant to help you choose.

NetTaskManagement TPL Dataflow
Task identity Named string key — look up, cancel, or delete any task by name at any time Anonymous — items are messages, no individual handles
Lifecycle control Explicit Register → Start → Cancel → CheckCompleted → Delete with status at every step Complete the block and await Completion; no per-item lifecycle
DI integration AddTaskManagement() registers ITaskManagement as a scoped service Manual construction
Cancellation granularity Per-task: cancel one, a filtered subset, or all — with per-task failure reporting Shared token: cancel the whole block at once
Threading model LongRunning: dedicated OS thread per task — all N workers start simultaneously, no pool pressure. None (default): thread-pool thread, lower startup cost Thread-pool threads — pool injection heuristic limits how many workers can start at once
Blocking-work throughput With LongRunning: ~3× faster than Dataflow at N=100 for blocking tasks — all threads start and block in parallel, no injection delay Pool injection batches workers when N > min-threads; total time scales with N / pool-size
Throughput focus Long-running named workers (background jobs, daemons, named pipelines) High-volume anonymous item streams (fan-out, fan-in, transform chains)
Observability Built-in disposal queue — capture task name, id, and final status when a task is removed No built-in disposal queue
Memory reclaim DeleteTask forces GC.Collect after disposal — guarantees memory is freed GC-managed by the runtime

Choose NetTaskManagement (LongRunning) when your workers block (I/O, sleep, external calls) and N exceeds the thread-pool minimum — all workers start simultaneously with no injection delay.
Choose NetTaskManagement (None) when tasks are short-lived and pool availability is not a concern — lower startup cost with named handles and per-task lifecycle control.
Choose TPL Dataflow when you are processing a high-volume stream of anonymous items through a pipeline graph.

View live benchmark charts — NTM vs Dataflow

Packages

Package NuGet
NetFramework.Tasks.Management Core implementation
NetFramework.Tasks.Management.Abstractions Interfaces, enums, and models only

Installation

dotnet add package NetFramework.Tasks.Management

If you only need the ITaskManagement contract (e.g. in a project that wires up DI but doesn't consume the implementation directly):

dotnet add package NetFramework.Tasks.Management.Abstractions

Quick start

Register the service

services.AddTaskManagement();

Inject and use ITaskManagement

public class MyService
{
    private readonly ITaskManagement _tasks;

    public MyService(ITaskManagement tasks) => _tasks = tasks;

    public void RunJob(string jobName)
    {
        var cts = new CancellationTokenSource();

        _tasks.RegisterTask(jobName, state =>
        {
            var token = ((CancellationTokenSource)state).Token;
            while (!token.IsCancellationRequested)
            {
                // do work
            }
        }, cts);

        _tasks.StartTask(jobName);
    }

    public void CancelJob(string jobName) => _tasks.CancelTask(jobName);
}

API reference

All methods return TaskManagementStatus — an enum value instead of throwing exceptions for expected outcomes.

Method Description
RegisterTask(name, action, cts) Registers a new task by name
StartTask(name) Starts a previously registered task
CancelTask(name) Requests cancellation via the task's CancellationTokenSource
CheckTaskStatusCompleted(name, retry, timeoutMs) Polls until the task completes or the timeout elapses
DeleteTask(name, sendToQueue) Disposes the task and removes it from the registry; optionally enqueues disposal metadata
DequeueTaskDisposedDataModel(out model) Dequeues one disposal record for external monitoring
GetTasksStatus() Returns a snapshot Dictionary<string, TaskStatus> of all live tasks
CancelAllTasks(except, ref failed) Cancels all tasks, optionally skipping a list of names
CheckAllTaskStatusCompleted(except, ref statuses, retry, timeoutMs) Polls completion for all tasks, optionally skipping a list of names
GetCancellationTokenSource(name, ref cts) Retrieves the CancellationTokenSource for a registered task

Status enum highlights

Status Meaning
Added Task registered successfully
Started Task started successfully
Canceled Cancellation requested
Completed Task reached a completion state
NotCompleted Polling timed out before completion
Deleted Task disposed and removed
TaskNotFound No task registered under that name
AlreadyRegistered A task with that name already exists

Observability queue

When DeleteTask(name, sendToQueue: true) is called, a TaskDisposedDataModel record (task name, id, final status, disposed flag) is appended to an internal ConcurrentQueue. Dequeue and forward it to your logging or monitoring platform however you like:

if (_tasks.DequeueTaskDisposedDataModel(out var record) == TaskManagementStatus.ObjectInfoDequeued)
{
    logger.LogInformation("Task {Name} (id={Id}) finished with status {Status}", 
        record.TaskName, record.TaskId, record.TaskStatus);
}

Benchmarks

Performance is tracked automatically on every push to main and published as interactive charts:

View benchmark charts

Benchmark class What it measures
LifecycleBenchmarks Each lifecycle stage in isolation: Register, Start, Cancel, Delete, and full end-to-end
GetTasksStatusBenchmarks Dictionary snapshot cost at 1, 10, and 50 tasks
CancelAllTasksBenchmarks Parallel.ForEach cancellation fan-out at 1, 10, and 50 tasks
DataflowComparisonBenchmarks Head-to-head vs TPL Dataflow across four scenarios at N = 10, 50, 100: start N workers, cancel N workers, process N short-lived items, and parallel blocking-work throughput. Each NTM scenario has two variants — _LongRunning (dedicated OS thread) and _Pool (thread-pool thread) — so you can pick the right threading model for your use case

All benchmarks run on net8.0, net9.0, and net10.0 with [MemoryDiagnoser] enabled (reports allocated bytes per operation).

To run the comparison benchmarks locally (Release mode required):

dotnet run -c Release -f net9.0 --project benchmarks/NetFramework.Tasks.Management.Benchmarks/ -- --filter '*DataflowComparison*'

To run everything:

dotnet run -c Release -f net8.0 --project benchmarks/NetFramework.Tasks.Management.Benchmarks/ -- --filter '*'

See benchmarks/README.md for full run instructions and filter examples.

Examples

Example Description
Financial Order Processing Multi-threaded market order pipeline — producer, 5 trading desk workers, live monitor, graceful shutdown, and observability queue drain

License

MIT © Jose Luis Guerra Infante

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Sponsor this project

  •  

Packages

 
 
 

Contributors