Asc.Utils.Needle 3.0.1

dotnet add package Asc.Utils.Needle --version 3.0.1
                    
NuGet\Install-Package Asc.Utils.Needle -Version 3.0.1
                    
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="Asc.Utils.Needle" Version="3.0.1" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Asc.Utils.Needle" Version="3.0.1" />
                    
Directory.Packages.props
<PackageReference Include="Asc.Utils.Needle" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Asc.Utils.Needle --version 3.0.1
                    
#r "nuget: Asc.Utils.Needle, 3.0.1"
                    
#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.
#:package Asc.Utils.Needle@3.0.1
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Asc.Utils.Needle&version=3.0.1
                    
Install as a Cake Addin
#tool nuget:?package=Asc.Utils.Needle&version=3.0.1
                    
Install as a Cake Tool

Asc.Utils.Needle

Asc.Utils.Needle is a small, focused library that simplifies multithreading and background work. Using a lightweight factory (Pincushion.Instance) you can obtain workers that run jobs using well-known concurrency patterns: semaphore-based workers, parallel workers and reusable job processors.

Parallel and semaphore workers implementations share the same interfaces so you can swap implementations, mock for tests, or integrate them into DI containers.

Advantages over hand-rolling concurrency

  • Unified, well-tested abstractions: no more repeating the same cancellation, synchronization anderror-aggregation code.
  • Error handling: job exceptions are collected and surfaced as an AggregateException so you can handle failures centrally.
  • Cancellation and observability: workers expose a CancellationToken, events and/or INotifyPropertyChanged (non-slim variants) to observe progress or cancellation.
  • Testability: interfaces make it easy to mock behaviour in unit tests.
  • Fewer subtle bugs: the library handles thread-safety, worker lifecycle and resource disposal for you.

Which worker to use

Pick the worker that matches your workload and observability needs:

  • Semaphore-style workers (SemaphoreWorker, SemaphoreWorkerSlim)

    • Limit the number of concurrent jobs with a semaphore. Ideal for I/O-heavy workloads (HTTP calls, database requests) where you want to bound concurrency.
    • SemaphoreWorkerSlim is the lightweight, high-performance variant (minimal API), suitable for backend or batch scenarios.
    • SemaphoreWorker (non-slim) exposes progress counters and INotifyPropertyChanged for UI or diagnostics.
  • Parallel workers (ParallelWorker, ParallelWorkerSlim)

    • Execute jobs in parallel without an explicit concurrency limit. Good for CPU-light tasks where maximum parallelism is desired.
    • ParallelWorkerSlim = minimal API; ParallelWorker = full API with counters and events.
  • Job processors (NeedleJobProcessor, NeedleJobProcessorSlim)

    • Long-lived processors with an internal thread pool that accept jobs dynamically. Use them when jobs are produced continuously and you want a reusable pool.
    • Slim = minimal, low-overhead; non-slim = additional counters and PropertyChanged notifications.

Slim vs non-slim

  • Slim versions prioritize throughput and low allocations and offer a minimal API surface.
  • Non-slim versions add observability (properties, events, INotifyPropertyChanged) and are useful for UIs or monitoring scenarios.

Quick capability summary (compact, GitHub-friendly)

Each worker type is listed with short capability lines to avoid wide columns and improve readability on GitHub.

  • SemaphoreWorker

    • Bounded concurrency: Yes
    • Dynamic enqueue: No
    • INotifyPropertyChanged: Yes
    • Counters: Yes
    • Low-overhead: No
  • SemaphoreWorkerSlim

    • Bounded concurrency: Yes
    • Dynamic enqueue: No
    • INotifyPropertyChanged: No
    • Counters: No
    • Low-overhead: Yes
  • ParallelWorker

    • Bounded concurrency: No
    • Dynamic enqueue: No
    • INotifyPropertyChanged: Yes
    • Counters: Yes
    • Low-overhead: No
  • ParallelWorkerSlim

    • Bounded concurrency: No
    • Dynamic enqueue: No
    • INotifyPropertyChanged: No
    • Counters: No
    • Low-overhead: Yes
  • NeedleJobProcessor

    • Bounded concurrency: Yes
    • Dynamic enqueue: Yes
    • INotifyPropertyChanged: Yes
    • Counters: Yes
    • Low-overhead: No
  • NeedleJobProcessorSlim

    • Bounded concurrency: Yes
    • Dynamic enqueue: Yes
    • INotifyPropertyChanged: No
    • Counters: No
    • Low-overhead: Yes

Installation

Install from NuGet (dotnet CLI):

dotnet add package Asc.Utils.Needle

Or using the Package Manager Console:

Install-Package Asc.Utils.Needle

You can also reference the package in your csproj:

<PackageReference Include="Asc.Utils.Needle" Version="2.1.0" />

Examples

All examples use the Pincushion.Instance factory to obtain workers.

  • Semaphore worker (slim) � bounded concurrency (backend example):
using Asc.Utils.Needle;

using var worker = Pincushion.Instance.GetSemaphoreWorkerSlim(maxThreads: 5);

foreach (var url in urls)
    worker.AddJob(async () => await HttpGetAsync(url));

try
{
    await worker.RunAsync();
}
catch (AggregateException ae)
{
    // examine ae.InnerExceptions
}
  • Semaphore worker (full) � with progress notifications (frontend example):
using Asc.Utils.Needle;

using var worker = Pincushion.Instance.GetSemaphoreWorker(maxThreads: 5);

worker.PropertyChanged += (_, e) =>
{
    var prop = worker.GetType().GetProperty(e.PropertyName);
    var value = prop is not null ? prop.GetValue(worker) : null;
    Console.WriteLine($"{e.PropertyName} = {value}");
};

worker.AddJob(() => DoSyncWork());
worker.AddJob(async () => await DoAnotherWorkAsync());

await worker.RunAsync();
  • Parallel worker (slim):
using Asc.Utils.Needle;

using var worker = Pincushion.Instance.GetParallelWorkerSlim();

for (int i = 0; i < 100; i++)
    worker.AddJob(async () => await DoSmallAsyncWork());

await worker.RunAsync();
  • Parallel worker (full):
using Asc.Utils.Needle;

using var worker = Pincushion.Instance.GetParallelWorker();

worker.PropertyChanged += (_, e) =>
{
    var prop = worker.GetType().GetProperty(e.PropertyName);
    var value = prop is not null ? prop.GetValue(worker) : null;
    Console.WriteLine($"{e.PropertyName} = {value}");
};

worker.AddJob(() => DoSyncWork());

await worker.RunAsync();
  • NeedleJobProcessorSlim � dynamic job stream with internal pool:
using Asc.Utils.Needle;

using var processor = Pincushion.Instance.GetJobProcessorSlim(threadPoolSize: Environment.ProcessorCount);
processor.Start();

processor.ProcessJob(async () => await DoBackgroundWork());
processor.ProcessJob(SomeVoidMethodToRun);

processor.Pause();

// do some other stuff in between

processor.Resume();

// stop when done
await processor.DisposeAsync();
  • NeedleJobProcessor (full) � same as above but with counters and property notifications:
using Asc.Utils.Needle;

using var processor = Pincushion.Instance.GetJobProcessor(threadPoolSize: 4);

processor.PropertyChanged += (_, e) =>
{
    var prop = processor.GetType().GetProperty(e.PropertyName);
    var value = prop is not null ? prop.GetValue(processor) : null;
    Console.WriteLine($"{e.PropertyName} = {value}");
};

processor.Start();
processor.ProcessJob(() => Task.Delay(100));

await processor.DisposeAsync();

More information

Read the public interfaces for details on behavior and guarantees:

  • INeedleWorkerSlim / INeedleWorker
  • INeedleJobProcessorSlim / INeedleJobProcessor

They document cancellation semantics, error aggregation and lifecycle constraints.


Icon attribution

Icon from Flaticon:

<a href="https://www.flaticon.com/free-icons/sew" title="sew icons">Sew icons created by Pixel perfect - Flaticon</a>

Product Compatible and additional computed target framework versions.
.NET net10.0 is compatible.  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. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • net10.0

    • No dependencies.

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
3.0.1 181 11/23/2025
3.0.0 165 11/23/2025
2.1.0 222 3/15/2025
2.0.1 192 1/30/2025
2.0.0 172 1/30/2025
1.1.2 190 11/7/2024
1.1.1 167 11/7/2024
1.1.0 154 11/7/2024
1.0.1 331 1/8/2024
1.0.0 214 1/8/2024

net10.0 version