В этом случае вы используете CancellationToken
неправильно.CancellationToken
- это структура, которая упаковывает CancellationTokenSource
следующим образом:
public bool IsCancellationRequested
{
get
{
return source != null && source.IsCancellationRequested;
}
}
Поскольку это структура, она передается вокруг по значению , то есть той, которую вы храните в своем списке. не совпадает с экземпляром, который есть у вашего Coroutine
.
Типичный способ обработки отмены - создать CancellationTokenSource и передать его Token
вокруг.Всякий раз, когда вы хотите отменить его, вы просто вызываете метод .Cancel()
для CancellationTokenSource
.Причина этого заключается в том, что CancellationToken
может только быть отменено через ссылку «источник», а не потребителями токена.
В вашем случае высоздание токена без источника вообще, поэтому я бы предложил внести следующие изменения:
Прежде всего, измените список cancelTokens
на:
List<CancellationTokenSource>
Далее измените свойOnButtonClick()
метод выглядит следующим образом:
public void OnButtonClick()
{
// You should probably call `cancelAllCoroutines()` here
cancelAllCoroutines();
var cancellationTokenSource = new CancellationTokenSource();
cancelTokens.Add(cancellationTokenSource);
Coroutine co = StartCoroutine(StartCountdown(cooldownDuration, cancellationTokenSource.Token));
myCoroutines.Add(co);
}
И, наконец, измените ваш метод cancelAllCoroutines()
на этот:
public static void CancelAllCoroutines()
{
Debug.Log("cancelling all coroutines with total of : " + cancelTokens.Count);
foreach (CancellationTokenSource ca in cancelTokens)
{
ca.Cancel();
}
// Clear the list as @Jack Mariani mentioned
cancelTokens.Clear();
}
Я бы предложил прочитать документы по ОтменаТокены или, как предложено @JLum, используйте метод StopCoroutine , который предоставляет Unity.
РЕДАКТИРОВАТЬ: я забыл упомянуть, что рекомендуется утилизировать CancallationTokenSources
прибольше не используется, чтобы предотвратить утечки памяти.Я бы порекомендовал сделать это в OnDestroy () хук для вашего MonoBehaviour
, например, так:
private void OnDestroy()
{
foreach(var source in cancelTokens)
{
source.Dispose();
}
}
РЕДАКТИРОВАТЬ 2: Как @Jack Mariani упомянул в своем ответе, несколько CancellationTokenSources
в этом случае излишне.Все, что вам действительно нужно было бы сделать, это иметь более детальный контроль над тем, что Coroutine
отменяется.В этом случае вы отменяете их все за один раз, так что да, оптимизация будет заключаться в создании только одного из них.Здесь можно сделать несколько оптимизаций, но они выходят за рамки этого вопроса.Я не включил их, потому что чувствовал, что это раздувает этот ответ больше, чем необходимо.
Однако я бы поспорил с его утверждением о том, что CancellationToken
«в основном предназначен для Задачи».Выделяются прямо из первой пары строк в MSDN docs :
Начиная с .NET Framework 4, .NET Framework использует унифицированную модель для совместного отмены асинхронных или длительные синхронные операции .Эта модель основана на облегченном объекте , который называется токеном отмены
CancellationTokens
- это облегченные объекты.По большей части они просто Structs
, которые ссылаются на CancellationTokenSource
.«Накладные расходы», о которых упоминается в его ответе, ничтожны и, на мой взгляд, полностью стоят того, чтобы учесть читабельность и намерение.
Вы могли бы передать нагрузку booleans
синдексы или подписка на события с использованием литералов string
, и эти подходы будут работать.Но какой ценой?Запутанный и трудно читаемый код?Я бы сказал, что оно того не стоит.
Хотя выбор в конечном итоге за вами.