В продолжение вопроса, который я задал пару дней назад:
я пытаюсь сделать остановку - возобновление работы задачи (Task).
Пример я написал на WinForms, но вообще не важно, как именно - мне просто хочется понять, как работать с задачей. Но, как я понимаю, если задача перешла в состояние cancelled, то возобновить её выполнение не получается.
У меня в примере - форма с двумя кнопками, и текстбоксом, куда просто печатаются числа.
И старт-стоп работает только один раз.
Как это сделать более правильно?
Спасибо!
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
token = cancelTokenSource.Token;
startBtn.Click += StartBtn_Click;
stopBtn.Click += StopBtn_Click;
}
CancellationTokenSource cancelTokenSource = new CancellationTokenSource();
CancellationToken token;
int cnt = 0;
private void StartBtn_Click(object? sender, EventArgs e) {
var t = Task.Run(() => { do { ModifyTextBox(); Thread.Sleep(1000); } while (!token.IsCancellationRequested); }, token);
}
private void StopBtn_Click(object? sender, EventArgs e) {
cancelTokenSource.Cancel();
}
private void ModifyTextBox() {
Action modAction = () =>
{
textBox1.Text += (++cnt).ToString() + Environment.NewLine;
};
if (textBox1.InvokeRequired)
textBox1.Invoke(modAction);
else
modAction();
}
}
Продолжение
я попробовал учесть рекомендации и написал такой код. Теперь я всю работу вынес в Action, а на основе Action стараюсь каждый раз создать при нажатии на кнопку НОВУЮ задачу. Даже объект task сделал локальным.
И всё таки получаю сообщение об ошибке 'Start may not be called on a task that has completed' при попытке второй раз нажать на startBtn.
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
token = cancelTokenSource.Token;
startBtn.Click += StartBtn_Click;
stopBtn.Click += StopBtn_Click;
myAction = () =>
{
do {
ModifyTextBox();
Thread.Sleep(1000);
} while (!token.IsCancellationRequested);
};
}
CancellationTokenSource cancelTokenSource = new CancellationTokenSource();
CancellationToken token;
int cnt = 0;
Action myAction;
private async void StartBtn_Click(object? sender, EventArgs e) {
Task task = new Task(myAction, token);
task.Start();
await task;
task.Dispose();
task = null;
}
private void StopBtn_Click(object? sender, EventArgs e) {
cancelTokenSource.Cancel();
}
private void ModifyTextBox() {
Action modAction = () =>
{
textBox1.Text += (++cnt).ToString() + Environment.NewLine;
};
if (textBox1.InvokeRequired)
textBox1.Invoke(modAction);
else
modAction();
}
}
Третй и четвертый вариант: после советов, которые мне дали в комментариях, я всё переписал под создание новой задачи при каждом нажатии на кнопку Start.
И передо мной был выбор: как же прерывать задачу? Есть два способа - просто return'ом после того, как стало истинным token.IsCancellationRequested или взывая token.ThrowIfCancellationRequested();
К сожалению, магия работает только в руках волшебников: при первом способе - задача во второй раз не запускается с сообщением "A task was canceled".
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
startBtn.Click += StartBtn_Click;
stopBtn.Click += StopBtn_Click;
token = cancelTokenSource.Token;
myAction = () => {
do {
ModifyTextBox();
if (token != null && token.IsCancellationRequested)
return;
Thread.Sleep(1000);
} while (true);
};
}
CancellationTokenSource cancelTokenSource = new CancellationTokenSource();
CancellationToken token;
int cnt = 0;
Action myAction;
private async void StartBtn_Click(object? sender, EventArgs e) {
await Task.Run(myAction, token);
}
private void StopBtn_Click(object? sender, EventArgs e) {
cancelTokenSource.Cancel();
}
private void ModifyTextBox() {
Action modAction = () =>
{
textBox1.Text += (++cnt).ToString() + Environment.NewLine;
};
if (textBox1.InvokeRequired)
textBox1.Invoke(modAction);
else
modAction();
}
}
при втором способе при повторном нажатии на кнопку Start задача просто не продолжает работу. Ну, то есть, один раз кнопка срабаотывает - и всё.
Возможно, я не понимаю какой то тонкий момент, связанный со стартом задачи?
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
startBtn.Click += StartBtn_Click;
stopBtn.Click += StopBtn_Click;
token = cancelTokenSource.Token;
myAction = () => {
do {
ModifyTextBox();
if (token != null)
token.ThrowIfCancellationRequested();
Thread.Sleep(1000);
} while (true);
};
}
CancellationTokenSource cancelTokenSource = new CancellationTokenSource();
CancellationToken token;
int cnt = 0;
Action myAction;
private async void StartBtn_Click(object? sender, EventArgs e) {
try {
await Task.Run(myAction, token);
}
catch(OperationCanceledException ex) {
}
}
private void StopBtn_Click(object? sender, EventArgs e) {
cancelTokenSource.Cancel();
}
private void ModifyTextBox() {
Action modAction = () =>
{
textBox1.Text += (++cnt).ToString() + Environment.NewLine;
};
if (textBox1.InvokeRequired)
textBox1.Invoke(modAction);
else
modAction();
}
}
public async Task MyTask(){ //Делаем что-то }, далееawait MyTask();, готово, вот вам каждый раз новая задача, которая выполняет то, что внутри. Суть задачи - выполнить что-то асинхронно, не более. – EvgeniyZ Sep 10 '23 at 14:53await task, а повторноtask = new Task(myAction, token); await task;, ну илиawait Task.Run(...);. Вы не делаете новую задачу, вы все еще используете старую. – EvgeniyZ Sep 10 '23 at 16:09async Task Run(IProgress<DateTime> progress){ progress.Report(DateTime.Now); await Task.Delay(1000); }, далее за пределами делаемvar progress = new Progress<DateTime>(time => TextBox.Text = time.ToString());, ну и запускаем задачу, передав тудаprogress, готово. Как видите Task просто дает результат. – EvgeniyZ Sep 10 '23 at 17:52await Task<int> Run() { await Task.Delay(1000); return 1; }, а дальше простоvar result = await Run(); TextBox.Text = result.ToString();. Разделите ответственность, не мешайте все в одну кучу) – EvgeniyZ Sep 10 '23 at 17:53