У меня есть эта функция:
async Task RefreshProfileInfo(List<string> listOfPlayers)
// For each player in the listOfPlayers, checks an in-memory cache if we have an entry.
// If we have a cached entry, do nothing.
// If we don't have a cached entry, fetch from backend via an API call.
Эта функция вызывается очень часто, например:
await RefreshProfileInfo(playerA, playerB, playerC)
или
await RefreshProfileInfo(playerB, playerC, playerD)
или
await RefreshProfileInfo(playerE, playerF)
В идеале, если игроки не перекрывают друг друга, вызовы не должны влиять друг на друга (запрос PlayerE и PlayerF не должен блокировать запрос PlayerA, PlayerB, Player C). Однако, если игроки перекрывают друг друга, второй вызов должен ждать первого (запрашивающий PlayerB, Player C, PlayerD, должен дождаться, пока PlayerA, PlayerB, Player C завершат sh).
Однако, если это невозможно, я бы хотел, чтобы все вызовы были последовательными. (Я думаю, что они все еще должны быть асинхронными c, поэтому они не блокируют другие несвязанные части кода).
В настоящее время происходит то, что каждый RefreshProfileInfo выполняется параллельно, что приводит к тому, что каждый раз выполняется обращение к бэкенду (в данном примере 9 раз).
Вместо этого я хочу выполнить их последовательно, чтобы только первый вызов обращался к бэкенду, а последующие вызовы просто ударяли в кеш.
Какую структуру данных / подход я должен использовать? У меня проблемы с выяснением, как «соединить» отдельные звонки друг с другом. Я играл с Task.WhenAll (), а также с SemaphoreSlim, но не могу понять, как их правильно использовать.
Неудачная попытка
Идея, лежащая в основе моей неудачной попытки, заключалась в том, чтобы создать вспомогательный класс, в котором я мог бы вызвать функцию SequentialRequest (Task), и она последовательно выполняла бы все задачи, вызываемые таким образом.
List<Task> waitingTasks = new List<Task>();
object _lock = new object();
public async Task SequentialRequest(Task func)
{
var waitingTasksCopy = new List<Task>();
lock (_lock)
{
waitingTasksCopy = new List<Task>(waitingTasks);
waitingTasks.Add(func); // Add this task to the waitingTasks (for future SequentialRequests)
}
// Wait for everything before this to finish
if (waitingTasksCopy.Count > 0)
{
await Task.WhenAll(waitingTasksCopy);
}
// Run this task
await func;
}
Я думал, что это сработает, но "fun c" либо запускается мгновенно (вместо ожидания более ранних задач до конца sh), либо никогда не запускается вообще, в зависимости от того, как я назови это.
Если я вызываю его с помощью этого, он запускается мгновенно:
async Task testTask()
{
await Task.Delay(4000);
}
Если я вызываю его с помощью этого, он никогда не запускается:
Task testTask = new Task(async () =>
{
await Task.Delay(4000);
});