Member 11426986 Ответов: 2

Задача отмене с#


Мой исходный код выполняет задачу и импортирует txt-файлы. Мне нужно дать возможность отменить этот процесс, когда пользователь захочет. Я несколько раз пытался отменить акцию, но не могу.

Что я уже пробовал:

Task taskImport = null;

private void Menu_Load(object sender, EventArgs e)
        {
            taskImport = new Task(Readtxt);//Readtxt is a method
        }

private void BtnImport_Click(object sender, EventArgs e)
        {
            taskImport.Start();
        }

2 Ответов

Рейтинг:
2

Richard Deeming

Что-то вроде этого:

CancellationTokenSource _cts = null;

private void BtnImoprt_Click(object sender, EventArgs e)
{
    CancellationTokenSource newSource = new CancellationTokenSource();
    CancellationTokenSource oldSource = Interlocked.Exchange(ref _cts, newSource);
    if (oldSource != null)
    {
        oldSource.Cancel();
        oldSource.Dispose();
    }
    
    Task.Run(() => Readtxt(newSource.Token), newSource.Token);
}

private void BtnCancel_Click(object sender, EventArgs e)
{
    CancellationTokenSource oldSource = Interlocked.Exchange(ref _cts, null);
    if (oldSource != null)
    {
        oldSource.Cancel();
        oldSource.Dispose();
    }
}

private async Task Readtxt(CancellationToken cancellationToken)
{
    // Pass the cancellation token to any async methods that support it.
    // Regularly check its IsCancellationRequested property, or call its ThrowIfCancellationRequested method.
}
Отмена в управляемых потоках | Microsoft Docs[^]

Насколько это будет эффективно, будет зависеть от методов, которые вы используете в рамках проекта. Readtxt метод.


Member 11426986

Это метод Readtxt

пустота Readtxt()
{
строка ccte = txtFolderCTE.Text;
строку папку = нуль;
foreach (файловая система var в каталоге.Заражен(ccte, "*.в формате XML", searchoption указывает, нужно.AllDirectories))
{
FileInfo fi = новый FileInfo(файловая система);
каминью = Fi интернет.Полное имя;

пробовать
{
DirectXML dir = новый DirectXML();
dir.direcionarXML(папка, "");
}
catch (исключение m)
{
registerLog(м. Сообщение, папку);
}
}
}

Richard Deeming

Этот метод не имеет никакого смысла. Почему вы перечисляете каждый XML-файл в папке, когда вы ничего не делаете с этими файлами? Вы просто вызываете один и тот же метод с одними и теми же параметрами снова и снова.

Member 11426986

Нет "DirectXML dir = новый DirectXML ();
dir.direcionarXML(папка, ""); ", он будет считывать XML-файлы и сохранять собранную информацию в базу данных.

Richard Deeming

Нигде в коде, который вы показали, вы не используете переменную цикла. Вы просматриваете каждый XML-файл в папке и вызываете тот же метод с те же параметры по одному разу для каждого файла.

Ничего в твоей жизни нет. try..catch блок использует fileSystem, fi, или caminho.

Петля не имеет никакого смысла.

Member 11426986

В foreach он будет извлекать все XML - файлы из каталога, который вы ввели в переменную ccte. Это позволит получить полный путь к каждому XML-файлу и передать его классу, который будет читать XML.
Извините, произошла ошибка редактирования.

FileInfo fi = новый FileInfo (файловая система);
папка = Fi интернет.Полное имя;

пробовать
{
DirectXML dir = новый DirectXML ();
dir.directXML (папка, "");
}
catch (исключение m)
{
registerLog (м. Сообщение, папку);
}

Richard Deeming

Ну, если только вы не можете сделать то же самое. directXML метод async самое лучшее , что вы можете сделать, это:

private void Readtxt(CancellationToken cancellationToken)
{
    string folderPath = txtFolderCTE.Text;
    foreach (string filePath in Directory.EnumerateFiles(folderPath, "*.xml", SearchOption.AllDirectories))
    {
        cancellationToken.ThrowIfCancellationRequested();
        
        try
        {
            DirectXML dir = new DirectXML();
            dir.directXML(filePath, "");
        }
        catch (Exception ex)
        {
            registerLog(ex.Message, filePath);
        }
    }
}
Я бы также задался вопросом, Нужно ли создавать новый экземпляр вашего DirectXML класс для каждого файла; но это другой вопрос.

Member 11426986

Я постараюсь это сделать. Но это вызывает у меня сомнения. Как я могу увидеть, выполняется ли эта задача?

Richard Deeming

Упрощенно говоря, вы можете сохранить задачу, возвращенную из Task.Run в поле и проверьте его состояние. Но может быть и лучший вариант, в зависимости от того, что вы на самом деле хотите сделать.

Member 11426986

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

Это событие click button.

CancellationTokenSource newSource = новый CancellationTokenSource();
CancellationTokenSource oldSource = заблокирован.Биржа(ref _cts, newSource);
если (древнимисточником != нуль)
{
старый источник.Отменить();
старый источник.Располагать();
}


Task.Run(() => Readtxt(источник новостей.Маркер, "КТР"), newSource.Знак);
Task.Run(() => Readtxt(источник новостей.Токен, "НФЭ"), источник новостей.Знак);

Richard Deeming

Не беспокойтесь о таймере - просто используйте его async код:

private void BtnCancel_Click(object sender, EventArgs e)
{
    CancellationTokenSource newSource = new CancellationTokenSource();
    CancellationTokenSource oldSource = Interlocked.Exchange(ref _cts, newSource);
    if (oldSource != null)
    {
        oldSource.Cancel();
        oldSource.Dispose();
    }
    
    // Discard the result:
    _ = ExecuteImportAsync(newSource.Token);
}

private async Task ExecuteImportAsync(CancellationToken cancellationToken)
{
    txtExecucaoPro.ForeColor = Color.FromArgb(0, 192, 0);
    txtExecucaoPro.Text = "PROCESSO EM EXECUÇÃO";
    try
    {
        Task cte = Task.Run(() => ExecutaLeitura(cancellationToken, "CTE"), cancellationToken);
        Task nfe = Task.Run(() => ExecutaLeitura(cancellationToken, "NFE"), cancellationToken);
        await Task.WhenAll(cte, nfe);
    }
    finally
    {
        txtExecucaoPro.Text = "PROCESSO COMPLETO";
    }
}
Рассуждения, лежащие в основе части "отбросить результат", см.:
https://github.com/davidfowl/AspNetCoreDiagnosticScenarios/blob/master/AsyncGuidance.md#timer-callbacks[^]

Рейтинг:
0

George Swan

Вы не хотите создавать новое Tasks и управляя ими напрямую, лучше вызвать метод, который возвращает Task.
FileStream.ReadAsync() возвращает Task, поддерживает асинхронное считывание данных и может быть отменено.
Вы можете использовать его вот так.

private async Task<string>ReadFileAsync(string filePath,CancellationToken token)
 {

   byte[] byteArray;
   FileStream fileStream = null;
   try
      {
       fileStream = File.Open(filePath, FileMode.Open);
       byteArray = new byte[fileStream.Length];
       //this method call throws an OperationCancelledException if cancelled
       //At this point control is returned to the UI thread
       //until the method completes
       await fileStream.ReadAsync(byteArray, 0, (int)fileStream.Length, token);
       string text=Encoding.ASCII.GetString(byteArray);
       return text;
      }

   finally
      {
        if (fileStream != null) fileStream.Dispose();            }
      }

Вы можете начать загрузку данных в обработчике щелчка кнопки Пуск.
private CancellationTokenSource cts;

private async void Start(object sender, RoutedEventArgs e)
 {
   List<string> texts = new List<string>();
   string filename = @"c:\Temp\Io.txt";
   //disable the start button and enable the cancel button
   cts = new CancellationTokenSource();
   //Or, to test the cancellation process,
   //make cts cancel itself after 20 millisecs
   //cts = new CancellationTokenSource(20);
   try
     {
      //simulate reading 100 files
      for (int i = 0; i < 100; i++)
       {
        //pass in the cancellation token to the file read method
        string text = await ReadFileAsync(filename, cts.Token);
        texts.Add(text);
       }
      }
    catch (OperationCanceledException )
       {
         //do something with the cancelled data
         texts.Clear();
       }

          //......
   }

Используйте обработчик щелчка кнопки отмены для отмены чтения файла.То CancellationTokenSource расходуется после отмены бронирования и не может быть использован повторно.
private void Cancel(object sender, RoutedEventArgs e)
{

    if (cts != null) cts.Cancel();

}