ktsu.Invoker 1.1.1

Prefix Reserved
dotnet add package ktsu.Invoker --version 1.1.1
                    
NuGet\Install-Package ktsu.Invoker -Version 1.1.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="ktsu.Invoker" Version="1.1.1" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="ktsu.Invoker" Version="1.1.1" />
                    
Directory.Packages.props
<PackageReference Include="ktsu.Invoker" />
                    
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 ktsu.Invoker --version 1.1.1
                    
#r "nuget: ktsu.Invoker, 1.1.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 ktsu.Invoker@1.1.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=ktsu.Invoker&version=1.1.1
                    
Install as a Cake Addin
#tool nuget:?package=ktsu.Invoker&version=1.1.1
                    
Install as a Cake Tool

ktsu.Invoker

A .NET library that ensures delegates are executed on the intended thread, simplifying thread management in UI and graphics applications.

License NuGet NuGet Downloads Build Status GitHub Stars

Introduction

Invoker is a .NET library that provides methods to ensure delegates are executed on the intended thread. It is designed to simplify task execution and thread management in .NET applications where delegates are required to run within a specific context, such as the UI thread in WPF or WinForms applications, or the window thread in OpenGL or DirectX applications.

Features

  • Thread-Safe Execution: Ensure delegates run on the intended thread
  • Synchronous & Asynchronous Support: Both blocking and non-blocking invocation patterns
  • Return Value Support: Easily retrieve results from cross-thread operations
  • Immediate Execution: Auto-detect if already on the target thread for optimal performance
  • Queue Management: Built-in task queue with controlled execution timing
  • Thread Ownership: Clear ownership model for execution contexts
  • Exception Propagation: Properly propagates exceptions across thread boundaries
  • Lightweight Design: Minimal overhead for performance-critical applications

Installation

Package Manager Console

Install-Package ktsu.Invoker

.NET CLI

dotnet add package ktsu.Invoker

Package Reference

<PackageReference Include="ktsu.Invoker" Version="x.y.z" />

Usage Examples

Basic Example

// Initialize an instance on the owning thread
var invoker = new Invoker();

// Queue a task from a different thread, which blocks until the delegate has been executed via DoInvokes() on the owning thread
invoker.Invoke(() => Console.WriteLine("Hello, World!"));

// Call DoInvokes() on the owning thread to execute queued tasks
invoker.DoInvokes();

// NOTE: If you queue from the owning thread the delegate will be executed immediately, bypassing DoInvokes()

// Delegates with return values are supported
string result = invoker.Invoke(() => "Hello, World!");

WPF UI Thread Example

using System.Windows;
using ktsu.Invoker;

public partial class MainWindow : Window
{
    private readonly Invoker _invoker = new Invoker();
    
    public MainWindow()
    {
        InitializeComponent();
        
        // Start a background operation
        Task.Run(() => BackgroundOperation());
    }
    
    private async Task BackgroundOperation()
    {
        // Simulate work
        await Task.Delay(1000);
        
        // Update UI from background thread safely
        _invoker.Invoke(() => {
            StatusTextBlock.Text = "Operation Completed!";
            ResultListBox.Items.Add("Background task result");
        });
    }
    
    // Call this in your UI event loop or dispatcher
    private void ProcessEvents()
    {
        _invoker.DoInvokes();
    }
}

Asynchronous Invocation

using ktsu.Invoker;

// Create invoker on the main thread
var invoker = new Invoker();

// From background thread
await Task.Run(async () => {
    // Queue task and continue without waiting
    invoker.BeginInvoke(() => Console.WriteLine("Processing in the background"));
    
    // Queue task and get Task for completion
    Task<string> resultTask = invoker.InvokeAsync(() => "Result from main thread");
    
    // Await the result
    string result = await resultTask;
    Console.WriteLine($"Got result: {result}");
});

// On main thread, execute pending operations
invoker.DoInvokes();

Game Loop Integration

using ktsu.Invoker;

public class Game
{
    private readonly Invoker _invoker = new Invoker();
    
    public void Run()
    {
        // Start rendering on the main thread
        while (true)
        {
            // Execute any queued operations from other threads
            _invoker.DoInvokes();
            
            // Perform rendering
            Render();
            
            // Process events, etc.
        }
    }
    
    private void Render() 
    {
        // OpenGL/DirectX rendering code here
    }
    
    // Call this from other threads
    public void QueueTextureLoad(string texturePath)
    {
        // OpenGL/DirectX resources often need to be created on the main thread
        _invoker.BeginInvoke(() => LoadTextureOnMainThread(texturePath));
    }
    
    private void LoadTextureOnMainThread(string texturePath)
    {
        // Load texture using OpenGL/DirectX APIs
    }
}

API Reference

Invoker Class

The main class that manages execution of delegates on the intended thread.

Properties
Name Type Description
IsInvokerThread bool Returns true if the current thread is the thread that owns the invoker
HasPendingInvokes bool Returns true if there are any pending invocations waiting to be processed
Methods
Name Parameters Return Type Description
Invoke<T> Func<T> func T Executes the function on the owner thread and returns its result, blocking if called from another thread
Invoke Action action void Executes the action on the owner thread, blocking if called from another thread
BeginInvoke Action action void Queues an action to be executed on the owner thread without waiting for completion
InvokeAsync<T> Func<T> func Task<T> Queues a function to be executed on the owner thread and returns a Task that completes with the result
InvokeAsync Action action Task Queues an action to be executed on the owner thread and returns a Task that completes when the action is done
DoInvokes void Processes all pending invocations (must be called from the owner thread)

Advanced Usage

Thread Synchronization

Invoker provides a clean way to synchronize access to resources that must be accessed from a specific thread:

public class ResourceManager
{
    private readonly Invoker _invoker = new Invoker();
    private readonly Dictionary<string, Resource> _resources = new Dictionary<string, Resource>();
    
    // This method can be called from any thread
    public Resource GetResource(string id)
    {
        return _invoker.Invoke(() => {
            if (!_resources.TryGetValue(id, out var resource))
            {
                resource = new Resource(id);
                _resources[id] = resource;
            }
            return resource;
        });
    }
    
    // Call this regularly on the owner thread
    public void Update()
    {
        _invoker.DoInvokes(); // Process any pending resource requests
        
        // Update resources
        foreach (var resource in _resources.Values)
        {
            resource.Update();
        }
    }
}

Custom Thread Identification

Sometimes you may need custom thread identification logic:

public class CustomInvoker : Invoker
{
    private readonly int _targetThreadId;
    
    public CustomInvoker(int targetThreadId)
    {
        _targetThreadId = targetThreadId;
    }
    
    // Override to provide custom thread identification
    protected override bool IsOwnerThread()
    {
        return Thread.CurrentThread.ManagedThreadId == _targetThreadId;
    }
}

Contributing

Contributions are welcome! Here's how you can help:

  1. Fork the repository
  2. Create your feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes (git commit -m 'Add some amazing feature')
  4. Push to the branch (git push origin feature/amazing-feature)
  5. Open a Pull Request

Please make sure to update tests as appropriate and adhere to the existing coding style.

License

This project is licensed under the MIT License - see the LICENSE.md file for details.

Acknowledgements

Special thanks to all contributors and the .NET community for their support.

Product Compatible and additional computed target framework versions.
.NET net5.0 is compatible.  net5.0-windows was computed.  net6.0 is compatible.  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 is compatible.  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 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. 
.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 (2)

Showing the top 2 NuGet packages that depend on ktsu.Invoker:

Package Downloads
ktsu.ImGuiApp

A comprehensive .NET library that provides complete application scaffolding for Dear ImGui applications, featuring window management, DPI-aware rendering, precision PID-controlled frame limiting with comprehensive auto-tuning, advanced font handling with Unicode/emoji support, texture management, and debug tooling. Built on Silk.NET for cross-platform OpenGL support and Hexa.NET.ImGui for modern Dear ImGui bindings.

ktsu.ImGui.App

Package Description

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.1.1 250 8/26/2025
1.1.1-pre.2 66 4/26/2025
1.1.1-pre.1 126 4/4/2025
1.1.0 1,524 3/30/2025
1.0.1-pre.2 81 3/29/2025
1.0.1-pre.1 466 3/25/2025
1.0.0 761 3/19/2025
0.0.1 492 3/25/2025

## v1.1.1 (patch)

Changes since v1.1.0:

- Remove Directory.Build.props and Directory.Build.targets files, delete unused PowerShell scripts, and add copyright headers to Invoker and InvokerTests classes. ([@matt-edmondson](https://github.com/matt-edmondson))
- Refactor project files and configurations for .NET 9 compatibility. Update indentation settings in .editorconfig, adjust .gitattributes and .gitignore for consistency, and remove deprecated polyfill code. Enhance Invoker class methods with Guard checks for null arguments. ([@matt-edmondson](https://github.com/matt-edmondson))
- Enhance .NET workflow with manual trigger support, update build steps for SonarQube integration, and improve error handling in PSBuild scripts. Adjust project files for .NET 9 compatibility and refine test cases for better exception handling. ([@matt-edmondson](https://github.com/matt-edmondson))
- Update project metadata and enhance documentation with detailed features and usage examples for the ktsu.Invoker library. ([@matt-edmondson](https://github.com/matt-edmondson))
- Refactor asynchronous task execution in InvokerTests to use Thread instead of Task.Run, ensuring tasks are queued correctly before assertions. ([@matt-edmondson](https://github.com/matt-edmondson))
- Update configuration files and scripts for improved build and test processes ([@matt-edmondson](https://github.com/matt-edmondson))
## v1.1.1-pre.2 (prerelease)

Changes since v1.1.1-pre.1:

- Sync .github\workflows\dotnet.yml ([@ktsu[bot]](https://github.com/ktsu[bot]))
- Sync .editorconfig ([@ktsu[bot]](https://github.com/ktsu[bot]))
- Sync .runsettings ([@ktsu[bot]](https://github.com/ktsu[bot]))
## v1.1.1-pre.1 (prerelease)

Changes since v1.1.0:

- Sync .editorconfig ([@ktsu[bot]](https://github.com/ktsu[bot]))
## v1.1.0 (minor)

Changes since v1.0.0:

- Update changelog script to include additional version check for the initial version ([@matt-edmondson](https://github.com/matt-edmondson))
- Add LICENSE template ([@matt-edmondson](https://github.com/matt-edmondson))
## v1.0.1-pre.2 (prerelease)

Changes since v1.0.1-pre.1:

- Sync scripts\make-changelog.ps1 ([@ktsu[bot]](https://github.com/ktsu[bot]))
- Sync scripts\make-version.ps1 ([@ktsu[bot]](https://github.com/ktsu[bot]))
## v1.0.1-pre.1 (prerelease)

Changes since v1.0.0:

- Update changelog script to include additional version check for the initial version ([@matt-edmondson](https://github.com/matt-edmondson))
## v1.0.0 (major)

No significant changes detected since v0.0.1.
## v0.0.1 (patch)

- Update sample project file to disable packing ([@matt-edmondson](https://github.com/matt-edmondson))
- Update changelog script to include additional version check for the initial version ([@matt-edmondson](https://github.com/matt-edmondson))
- Initial commit ([@matt-edmondson](https://github.com/matt-edmondson))
- Update README.md for ktsu.Invoker usage examples ([@matt-edmondson](https://github.com/matt-edmondson))
- Update README.md with improved usage examples ([@matt-edmondson](https://github.com/matt-edmondson))
- Remove exclusion of LICENSE.md from project ([@matt-edmondson](https://github.com/matt-edmondson))
- Fix DoInvokesSameThreadShouldExecuteAllTasks test ([@matt-edmondson](https://github.com/matt-edmondson))
- Update Sample.csproj to executable output type ([@matt-edmondson](https://github.com/matt-edmondson))