Skip to content

Instantly share code, notes, and snippets.

@skst
Last active June 21, 2024 21:43
Show Gist options
  • Save skst/f765212b97f3ed1701810a6e2884c692 to your computer and use it in GitHub Desktop.
Save skst/f765212b97f3ed1701810a6e2884c692 to your computer and use it in GitHub Desktop.
Easily switch between foreground and background threads
using System;
using System.Runtime.CompilerServices;
using System.Threading;
using System.Windows.Threading;
namespace Shared;
/// <summary>
/// Easily switch between foreground and background threads.
/// </summary>
/// <example>
/// ...we're on the foreground thread...
/// await Shared.ThreadSwitcher.ResumeBackgroundAsync();
/// ...do stuff in the background...
/// await Shared.ThreadSwitcher.ResumeForegroundAsync(TheWindow.Dispatcher);
///
/// // Assume we enter on the UI thread.
/// using (var connection = new Connection())
/// {
/// // Initialize on the UI thread since we need information from UI objects.
/// connection.Initialize(SomeParameter);
///
/// await ThreadSwitcher.ResumeBackgroundAsync();
/// // Execute on a background thread.
/// connection.Execute();
/// } // connection is disposed here
///
/// // Process the results on a background thread.
/// Process(connection.GetResults());
///
/// // Get back on the UI thread.
/// await ThreadSwitcher.ResumeForegroundAsync(App.Current.Dispatcher);
/// // ...access UI controls...
/// </example>
/// <see cref="https://devblogs.microsoft.com/oldnewthing/20190329-00/?p=102373" />
/// <seealso cref="https://devblogs.microsoft.com/oldnewthing/20190328-00/?p=102368" />
// For WPF
private struct DispatcherThreadSwitcher : INotifyCompletion
{
private readonly Dispatcher _dispatcher;
internal DispatcherThreadSwitcher(Dispatcher dispatcher) => _dispatcher = dispatcher;
public DispatcherThreadSwitcher GetAwaiter() => this;
public bool IsCompleted => _dispatcher.CheckAccess();
public void GetResult() { }
// Implement INotifyCompletion
public void OnCompleted(Action continuation) => _dispatcher.BeginInvoke(continuation);
}
// For both WPF and Windows Forms
private struct ThreadPoolThreadSwitcher : INotifyCompletion
{
public ThreadPoolThreadSwitcher GetAwaiter() => this;
public bool IsCompleted => (SynchronizationContext.Current is null);
public void GetResult() { }
// Implement INotifyCompletion
public void OnCompleted(Action continuation) => ThreadPool.QueueUserWorkItem(_ => continuation());
}
public class ThreadSwitcher
{
// For WPF
static public DispatcherThreadSwitcher ResumeForegroundAsync(Dispatcher dispatcher) => new DispatcherThreadSwitcher(dispatcher);
// For both WPF and Windows Forms
static public ThreadPoolThreadSwitcher ResumeBackgroundAsync() => new ThreadPoolThreadSwitcher();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment