Files
UnrealEngine/Engine/Source/Programs/Shared/EpicGames.Core/AsyncQueue.cs
2025-05-18 13:04:45 +08:00

82 lines
2.2 KiB
C#

// Copyright Epic Games, Inc. All Rights Reserved.
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Channels;
using System.Threading.Tasks;
namespace EpicGames.Core
{
/// <summary>
/// Manages a set of tasks run in the background on a fixed number of tasks. Similar to <see cref="AsyncThreadPoolWorkQueue"/>.
/// </summary>
public sealed class AsyncQueue : IAsyncDisposable
{
readonly List<Task> _workerTasks = [];
readonly Channel<Func<CancellationToken, Task>> _queuedTasks = Channel.CreateUnbounded<Func<CancellationToken, Task>>();
readonly CancellationTokenSource _cancellationSource = new CancellationTokenSource();
/// <summary>
/// Constructor
/// </summary>
/// <param name="numWorkers">Number of concurrent workers executing tasks</param>
public AsyncQueue(int numWorkers)
{
for (int idx = 0; idx < numWorkers; idx++)
{
_workerTasks.Add(Task.Run(() => RunWorkerAsync(), _cancellationSource.Token));
}
}
/// <summary>
/// Finish executing all the tasks and wait for them to complete
/// </summary>
/// <param name="cancellationToken">Cancellation token for the operation</param>
public async Task StopAsync(CancellationToken cancellationToken)
{
_queuedTasks.Writer.TryComplete();
await Task.WhenAll(_workerTasks).WaitAsync(cancellationToken);
}
/// <inheritdoc/>
public async ValueTask DisposeAsync()
{
await _cancellationSource.CancelAsync();
await StopAsync(CancellationToken.None).ConfigureAwait(false);
_cancellationSource.Dispose();
}
/// <summary>
/// Enqueue a task to be executed
/// </summary>
/// <param name="task"></param>
public void Enqueue(Func<CancellationToken, Task> task)
=> _queuedTasks.Writer.TryWrite(task);
/// <summary>
/// Worker executing tasks. Any exception encountered will be bubbled up as all calls are awaited.
/// </summary>
private async Task RunWorkerAsync()
{
try
{
await foreach (Func<CancellationToken, Task> task in _queuedTasks.Reader.ReadAllAsync(_cancellationSource.Token))
{
try
{
await task(_cancellationSource.Token);
}
catch
{
}
}
}
catch (OperationCanceledException)
{
}
}
}
}