Как отменить ожидание Task.Delay()?
Как вы можете видеть в этом коде:
public async void TaskDelayTest()
{
while (LoopCheck)
{
for (int i = 0; i < 100; i++)
{
textBox1.Text = i.ToString();
await Task.Delay(1000);
}
}
}
Я хочу, чтобы он устанавливал текстовое поле в строковое значение i
с одним вторым периодом, пока не установил значение LoopCheck
значение false
. Но что он делает, так это то, что он создает все итерации для всех, и даже если я устанавливаю значение LoopCheck в false, он все равно делает то, что он делает асинхронно.
Я хочу отменить всю ожидаемую Task.Delay()
когда я устанавливаю LoopCheck=false
. Как я могу отменить его?
Ответы
Ответ 1
Используйте перегрузку из Task.Delay
, которая принимает CancellationToken
public async void TaskDelayTest(CancellationToken token)
{
while (!token.IsCancellationRequested)
{
for (int i = 0; i < 100; i++)
{
textBox1.Text = i.ToString();
await Task.Delay(1000, token);
}
}
}
var tokenSource = new CancellationTokenSource();
TaskDelayTest(tokenSource.Token);
...
tokenSource.Cancel();
Ответ 2
Если вы собираетесь опросить, опрос на CancellationToken
:
public async Task TaskDelayTestAsync(CancellationToken token)
{
for (int i = 0; i < 100; i++)
{
textBox1.Text = i.ToString();
await Task.Delay(TimeSpan.FromSeconds(1), token);
}
}
Для получения дополнительной информации см. Документацию об аннулировании.
Ответ 3
using System;
using System.Threading;
using System.Threading.Tasks;
namespace ConsoleApp1
{
class Program
{
static DateTime start;
static CancellationTokenSource tokenSource;
static void Main(string[] args)
{
start = DateTime.Now;
Console.WriteLine(start);
TaskDelayTest();
TaskCancel();
Console.ReadKey();
}
public static async void TaskCancel()
{
await Task.Delay(3000);
tokenSource?.Cancel();
DateTime end = DateTime.Now;
Console.WriteLine(end);
Console.WriteLine((end - start).TotalMilliseconds);
}
public static async void TaskDelayTest()
{
tokenSource = new CancellationTokenSource();
try
{
await Task.Delay(2000, tokenSource.Token);
DateTime end = DateTime.Now;
Console.WriteLine(end);
Console.WriteLine((end - start).TotalMilliseconds);
}
catch (TaskCanceledException ex)
{
Console.WriteLine(ex.Message);
}
finally
{
tokenSource.Dispose();
tokenSource = null;
}
}
}
}
Ответ 4
Это может быть очень глупое и элементарное решение, но моя идея...
public async void TaskDelayTest()
{
while (LoopCheck)
{
for (int i = 0; i < 100; i++)
{
textBox1.Text = i.ToString();
await Delay(1000);
}
}
}
private async void Delay(int delayInMillisecond)
{
for(int i=0; i<delayInMillisecond; i++)
{
await Task.Delay(1)
if(!LoopCheck)
break;
}
}
Преимущество такого подхода состоит в том, что вам нужно установить LoopCheck
в false
чтобы достичь своей цели, в то время как другие подходы требуют одновременного использования CancellationTokenSource.Cancel()
. Да, это займет всего 1 миллисекунду, чтобы выйти из цикла, но никто этого не заметил.
Если вы хотите, чтобы ваша задержка была более точной, попробуйте следующий метод.
public async void TaskDelayTest()
{
while (LoopCheck)
{
for (int i = 0; i < 100; i++)
{
textBox1.Text = i.ToString();
await Task.Run(()=>Delay(1000));
}
}
}
private void Delay(int delayInMillisecond)
{
double delayInSec = (double) delayInMillisecond / 1000;
var sw = new Stopwatch();
sw.Start();
while(true){
double ticks = sw.ElapsedTicks;
double seconds = ticks / Stopwatch.Frequency;
if(seconds >= delayInSec || !LoopCheck)
break;
}
}