C# - Как правильно ожидать асинхронной передачи данных другим классом? - PullRequest
0 голосов
/ 22 марта 2020

У меня есть группа «рабочих», которым в какой-то момент нужен уникальный ключ от моего HTTP-сервера для продолжения выполнения. Эти ключи иногда могут поступать до того, как они понадобятся рабочим, и в этом случае я просто хочу предоставить работнику ключ, чтобы он мог продолжить выполнение. Однако если ключей не осталось, я хочу, чтобы выполнение кода в задаче было остановлено до тех пор, пока не будет предоставлен ключ. Я также хочу, чтобы ключи, которые были предоставлены первыми, использовались первыми.

Я пытался внедрить Singleton, где ключи могут быть переданы с HTTP-сервера и извлечены рабочими, но выполнение кажется заблокированным прямо сейчас.

Мой код:

Keyhandler.cs

public sealed class KeyHandler
{
    private static readonly KeyHandler instance = new KeyHandler();

    private static Dictionary<Website, Queue<string>> retrievedKeys = new Dictionary<Website, Queue<string>>();
    private static Dictionary<Website, Queue<TaskCompletionSource<string>>> waitingWorkers = new Dictionary<Website, Queue<TaskCompletionSource<string>>>();

    // Explicit static constructor to tell C# compiler
    // not to mark type as beforefieldinit
    static KeyHandler()
    {
    }

    private KeyHandler()
    {
        foreach (Website website in Enum.GetValues(typeof(Website)))
        {
            retrievedKeys.Add(website, new Queue<string>());
            waitingWorkers.Add(website, new Queue<TaskCompletionSource<string>>());
        }
    }

    public static KeyHandler Instance
    {
        get
        {
            return instance;
        }
    }

    public static void AddKey(Website website, string response)
    {
        if (waitingWorkers[website].Count > 0)
        {
            waitingWorkers[website].Dequeue().SetResult(response);
        }
        else
        {
            retrievedKeys[website].Enqueue(response);
        }
    }

    public static string RetrieveKey(Website website)
    {
        if (retrievedKeys[website].Count > 0)
        {
            return retrievedKeys[website].Dequeue();
        } 
        else
        {
            TaskCompletionSource<string> tcs = new TaskCompletionSource<string>();
            Task<string> t = tcs.Task;
            waitingWorkers[website].Enqueue(tcs);

            return t.Result;
        }
    }
}

Worker.cs

    public async Task Run()
    {
        ...
        string key = KeyHandler.RetrieveKey(Website.MyWebsite);
        // do something with key here
        ...
    }

HTTPServer.cs

private void Process(HttpListenerContext context)
{
    ...
    KeyHandler.AddKey(Website.MyWebsite, key);
    ...
}
...