AskalotLearnalot Ответов: 1

Как использовать thread.abort()


Я просто пытаюсь прервать поток, нажав кнопку, чтобы остановить процесс, если пользователь хочет, а не выйти из приложения
Это оригинальный код:
private void Abort_Click(object sender, EventArgs e)
        {
         //   thread1.Abort();
        }

        ///  Runs method 'Copy' in a new thread with passed arguments - on this way we separate it from UI thread otherwise it would freeze
        private void backgroundCopy_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e )
        {

            List<object> genericlist = e.Argument as List<object>;
            Thread thread1 = new Thread(() => Copy(genericlist[0].ToString(), genericlist[1].ToString()));
            thread1.Start();
            thread1.Join(); //Waiting for thread to finish
        }


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

Я попробовал прервать() поток из события нажатия кнопки, переместив поле потока из метода, таким образом, вы можете получить доступ из вашего события нажатия кнопки, но это вызывает много ошибок
object sender;
     System.ComponentModel.DoWorkEventArgs e;
     List<object> genericlist = e.Argument as List<object>;
     Thread thread1 = new Thread(() => Copy(genericlist[0].ToString(), genericlist[1].ToString()));



     private void Abort_Click(object sender, EventArgs e)
     {
         thread1.Abort();
     }

     ///  Runs method 'Copy' in a new thread with passed arguments - on this way we separate it from UI thread otherwise it would freeze
     private void backgroundCopy_DoWork( )
     {


         thread1.Start();
         thread1.Join(); //Waiting for thread to finish
     }

это то, что я сделал, но я получаю ошибку под e и genericlist : инициализация поля не может ссылаться на нестатическое поле, метод или свойство.

1 Ответов

Рейтинг:
6

Richard Deeming

Прерывание нити-это очень опасная вещь, и ее следует избегать любой ценой.

Вы используете BackgroundWorker, который уже выполняет работу в фоновом потоке. Начало другой поток, чтобы сделать эту работу, а затем заморозить начальный фоновый поток до тех пор, пока ваш новый поток не закончит, будет только уменьшать производительность вашего кода.

Забудьте о ручном нарезании резьбы и выполняйте работу в ручном режиме. DoWork обработчик событий.

Набор то WorkerSupportsCancellation собственность[^] к true Если вы хотите остановить работника, позвоните ему то CancelAsync метод[^]. Внутри вашего цикла проверьте то CancellationPending собственность[^] чтобы увидеть, была ли работа отменена, и остановить, если она была отменена.

private void Abort_Click(object sender, EventArgs e)
{
    backgroundCopy.CancelAsync();
}

private void backgroundCopy_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e)
{
    List<object> genericlist = e.Argument as List<object>;
    if (genriclist is null || genericlist.Count < 2) throw new ArgumentException();
    
    string source = Convert.ToString(genericlist[0]);
    string destination = Convert.ToString(genericlist[1]);
    
    // Do your work here, periodically checking the CancellationPending flag:
    if (backgroundCopy.CancellationPending)
    {
        e.Cancel = true;
        return;
    }
}


Richard Deeming

Почему вы определяете свойства в своем классе вместо того, чтобы устанавливать их на BackgroundWorker?

Уберите линии:

public bool WorkerSupportsCancellation { get; set; }
[System.ComponentModel.Browsable(false)]
public bool CancellationPending { get; }

Выберите пункт BackgroundWorker в конструкторе откройте его свойства и найдите WorkerSupportsCancellation собственность. Установите его в положение true.

Уберите линии:
Thread thread1 = new Thread(() => Copy(genericlist[0].ToString(), genericlist[1].ToString()));
thread1.Start();
thread1.Join();

Либо переместите код из вашего Copy метод в backgroundCopy_DoWork метод, или просто вызовите Copy метод непосредственно, без запуска другого потока.

В пределах кода, выполняющего копирование, периодически проверяйте backgroundCopy.CancellationPending свойство и остановка, если оно возвращается true.

Richard Deeming

Потому что у вас нет элемента управления под названием cancelButton на форме.

Удалите этот метод; в нем нет необходимости, так как вы уже отменяете работу из Abort_Click обработчик.

AskalotLearnalot

мне очень жаль, что у меня все еще есть проблемы после этого.

Richard Deeming

Так ты вообще не читал мой ответ?

Не используйте нить.

Регистрация backgroundCopy.CancellationPending периодически во время копирования, и прекратите копирование, если он вернется true.

AskalotLearnalot

Спасибо, что уделили мне время.

  private void Abort_Click(object sender, EventArgs e)
        {
            // Cancel the asynchronous operation.
            this.backgroundCopy.CancelAsync();
            // Disable the Cancel button.
            Abort.Enabled = false;
            cancel = !cancel;
        }


        ///  Runs method 'Copy' in a new thread with passed arguments - on this way we separate it from UI thread otherwise it would freeze
        private void backgroundCopy_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e)
        {

            //List<object> genericlist = e.Argument as List<object>;
            //Thread thread1 = new Thread(() => Copy(genericlist[0].ToString(), genericlist[1].ToString()));
            //thread1.Start();
            //thread1.Join(); //Waiting for thread to finish
            List<object> genericlist = e.Argument as List<object>;
            if (genericlist is null || genericlist.Count < 2) throw new ArgumentException();

            string source = Convert.ToString(genericlist[0]);
            string destination = Convert.ToString(genericlist[1]);
            if (backgroundCopy.CancellationPending)
            {
                e.Cancel = true;
                return;
            }

        }

У меня есть это, но я не могу понять, как вызвать метод копирования, как это делается в:
//Thread thread1 = new Thread(() => Copy(genericlist[0].ToString(), genericlist[1].Метод toString()));

Richard Deeming

Вы не можете понять, как вызвать метод в C#?

Copy(source, destination);

Но еще раз вам нужно будет проверить backgroundCopy.CancellationPending свойство периодически внутри вашего Copy метод, и остановить копирование, если он возвращает true.

AskalotLearnalot

О я был напуган этой строкой:

 //Thread thread1 = new Thread(() => Copy(genericlist[0].ToString(), genericlist[1].ToString())); 

Вот что я сделал в методе копирования, но он все еще не прерывается, я уверен, что сделал что-то не так.
 public void Copy(string sourceDirectory, string targetDirectory)
        {
            if (backgroundCopy.CancellationPending != true)
            {
              //Do code


               
            }
            else
            {
                MessageBox.Show("Progrss terminated");
                return;
            }
        }

Richard Deeming

Вам нужно периодически проверять флаг во время части "Do code", а не только один раз в начале.

Как вы это сделаете, будет зависеть от того, как выглядит часть "Do code".

AskalotLearnalot

это метод копирования:

public void Copy(string sourceDirectory, string targetDirectory)
        {
            if (backgroundCopy.CancellationPending != true)
            {


                DirectoryInfo diSource = new DirectoryInfo(sourceDirectory);
                DirectoryInfo diTarget = new DirectoryInfo(targetDirectory);
                string[] entries = null;

                try
                {
                    if (!diSource.ToString().Contains("System Volume Information") && !diSource.ToString().ToUpper().Contains("$RECYCLE.BIN"))
                    {
                        //Gets list of all files and directories (we need it for progress bar)
                        entries = GetFiles(sourceDirectory, "*").ToArray();
                    }
                }

                catch (UnauthorizedAccessException ex)
                {
                    //ok, so we are not allowed to dig into that directory. Move on.
                }
                int max = 1;
                if (entries != null && entries.Count() != 0)
                {
                    max = entries.Count();
                }
                //Using Invoke to prevent Cross thread exception
                Invoke(new Action(() => progressBar1.Maximum = max));




                Invoke(new Action(() => progressBar1.Step = 1));
                Invoke(new Action(() => progressBar1.Value = 0));



                CopyAll(diSource, diTarget, entries);
                Cursor.Current = Cursors.Default;
            }
            else
            {
                MessageBox.Show("Progrss terminated");
                return;
            }
        }

У меня есть этот метод CopyAll, который, как я думаю, я должен поместить в него проверку отмены:
  public void CopyAll(DirectoryInfo newsource, DirectoryInfo newtarget, string[] entries)
        {
            //if (cancel == true)
            //{

            //    return;
            //}
            Cursor.Current = Cursors.WaitCursor;
            Application.DoEvents();
            Directory.CreateDirectory(newtarget.FullName);
            //cancel = false;
            //while (cancel != true)
            //{
            // Copy each file into the new directory.

            foreach (FileInfo fi in newsource.GetFiles())
            {
                if (!fi.ToString().Contains("System Volume Information") && !fi.ToString().ToUpper().Contains("$RECYCLE.BIN"))
                {

                    Invoke(new Action(() => lblInfo.Text = "Stopped"));

                    //Using Invoke to prevent Cross thread exception
                    Invoke(new Action(() => this.lblInfo.Text = string.Format("{0}\\{1}", newsource.FullName, fi.Name)));


                    if (File.Exists(Path.Combine(newtarget.FullName, fi.Name)))
                    {
                        File.SetAttributes(Path.Combine(newtarget.FullName, fi.Name), FileAttributes.Normal);
                        File.Delete(Path.Combine(newtarget.FullName, fi.Name));
                    }
                    fi.CopyTo(Path.Combine(newtarget.FullName, fi.Name), true);
                    File.SetAttributes(Path.Combine(newtarget.FullName, fi.Name), FileAttributes.Normal);
                    Application.DoEvents();
                    var prval = Math.Min(progressBar1.Maximum, progressBar1.Value + 1);
                    Invoke(new Action(() => progressBar1.Value = prval));

                }
            }
            // Copy each subdirectory using recursion.

            foreach (DirectoryInfo diSourceSubDir in newsource.GetDirectories())
            {
                try
                {
                    if (!diSourceSubDir.ToString().Contains("System Volume Information") && !diSourceSubDir.ToString().ToUpper().Contains("$RECYCLE.BIN"))
                    {
                        DirectoryInfo ne

Richard Deeming

Во-первых, набор backgroundCopy.WorkerReportsProgress к true, и использовать ReportProgres способ и ProgressChanged событие для обновления индикатора выполнения. Тогда вам не нужно будет использовать Invoke.

private void backgroundCopy_ProgressChanged(object sender, System.ComponentModel.ProgressChangedEventArgs e)
{
    progressBar1.Maximum = (int)e.UserState;
    progressBar1.Step = 1;
    progressBar1.Value = e.ProgressPercentage;
}

public void Copy(string sourceDirectory, string targetDirectory)
{
    DirectoryInfo diSource = new DirectoryInfo(sourceDirectory);
    if (!diSource.Exists) return;
    if (diSource.FullName.Contains("System Volume Information")) return;
    if (diSource.FullName.Contains("$RECYCLE.BIN")) return;
    
    string[] entries;
    try
    {
        entries = Directory.GetFiles(diSource.FullName, "*");
    }
    catch (UnauthorizedAccessException)
    {
        return;
    }
    
    backgroundCopy.ReportProgress(0, entries.Length);
    
    CopyAll(diSource, diTarget, entries);
    Cursor.Current = Cursors.Default;
}
Теперь вам нужно проверить backgroundCopy.CancellationPending флаг периодически внутри вашего CopyAll метод.

Richard Deeming

public void CopyAll(DirectoryInfo newsource, DirectoryInfo newtarget, string[] entries)
{
    Cursor.Current = Cursors.WaitCursor;
    newtarget.Create();
    
    foreach (FileInfo fi in newsource.GetFiles())
    {
        // Check whether the copy has been cancelled:
        if (backgroundCopy.CancellationPending) return;
        
        if (fi.FullName.Contains("System Volume Information")) continue;
        if (fi.FullName.Contains("$RECYCLE.BIN")) continue;
        
        // Copy the file
        ...
    }
    
    foreach (DirectoryInfo diSourceSubDir in newsource.GetDirectories())
    {
        // Check whether the copy has been cancelled:
        if (backgroundCopy.CancellationPending) return;
        
        if (diSourceSubDir.FullName.Contains("System Volume Information")) continue;
        if (diSourceSubDir.FullName.Contains("$RECYCLE.BIN")) continue;
        
        try
        {
            string entries = Directory.GetFiles(diSource.FullName, "*");
            DirectoryInfo diTargetSubDir = new DirectoryInfo(Path.Combine(newtarget.FullName, diSourceSubDir.Name));
            CopyAll(diSourceSubDir, diTargetSubDir, entries);
        }
        catch (UnauthorizedAccessException)
        {
        }
    }
}

AskalotLearnalot

Спасибо вам за гораздо более эффективный код, который показывает разницу в опыте. Теперь моя проблема заключается в копии всего, что я знаю, только как проверить это так, как я показал в предыдущем комментарии с If{} else{} Я не знаю, как делать это постоянно, прежде чем каждый файл или папка будут скопированы.

Richard Deeming

Просто положи трубку. if внутри дома foreach блоки. Таким образом, он будет выполняться для каждого файла и каталога.

AskalotLearnalot

Вот что у меня есть. Он действительно завершает процесс, однако окно сообщения показывает около 6 раз, а затем останавливается. и прогресс останавливается.

  public void CopyAll(DirectoryInfo newsource, DirectoryInfo newtarget, string[] entries)
        {
            //if (cancel == true)
            //{

            //    return;
            //}
            Cursor.Current = Cursors.WaitCursor;
            Application.DoEvents();
            Directory.CreateDirectory(newtarget.FullName);
            //cancel = false;
            //while (cancel != true)
            //{
            // Copy each file into the new directory.

            foreach (FileInfo fi in newsource.GetFiles())
            {
                if (backgroundCopy.CancellationPending != true)
                {
                    if (!fi.ToString().Contains("System Volume Information") && !fi.ToString().ToUpper().Contains("$RECYCLE.BIN"))
                    {

                        Invoke(new Action(() => lblInfo.Text = "Stopped"));

                        //Using Invoke to prevent Cross thread exception
                        Invoke(new Action(() => this.lblInfo.Text = string.Format("{0}\\{1}", newsource.FullName, fi.Name)));


                        if (File.Exists(Path.Combine(newtarget.FullName, fi.Name)))
                        {
                            File.SetAttributes(Path.Combine(newtarget.FullName, fi.Name), FileAttributes.Normal);
                            File.Delete(Path.Combine(newtarget.FullName, fi.Name));
                        }
                        fi.CopyTo(Path.Combine(newtarget.FullName, fi.Name), true);
                        File.SetAttributes(Path.Combine(newtarget.FullName, fi.Name), FileAttributes.Normal);
                        Application.DoEvents();
                        var prval = Math.Min(progressBar1.Maximum, progressBar1.Value + 1);
                        Invoke(new Action(() => progressBar1.Value = prval));

                    }
                }
                else
                {
                    MessageBox.Show("Progress have been cancelled");
                    return;
                }
            }
            // Copy each subdirectory using recursion.

            foreach (DirectoryInfo diSourceSubDir in newsource.GetDirectories())
            {
                if (backgroundCopy.CancellationPending != true)
                {
                    try
                    {
                        if (!diSourceSubDir.ToString().Contains("System Volume Information") && !diSourceSubDir.ToString().ToUpper().Contains("$RECYCLE.BIN"))
                        {
                            DirectoryInfo nextTargetSubDir =
                                newtarget.CreateSubdirectory(diSourceSubDir.Name);
                            CopyAll(diSourceSubDir, nextTargetSubDir, entries);
                            var prval = Math.Min(progressBar1.Maximum, progressBar1.Value + 1);
                            Invoke(new Action(() => progressBar1.Value = prval));

                        }

                        else
                        {
                            var prval = Math.Min(progressBar1.Maximum, progressBar1.Value + 1);
                            Invoke(new Action(() => progressBar1.Value = prval));
                        }
                    }

                    catch (Exception ex) { MessageBox.Show(ex.Message); }
                }
                else
                {
                    MessageBox.Show("Progress have been cancelled");
                    return;
                }
            }

  }

AskalotLearnalot

Также боковое примечание: что касается нового метода копирования, который вы предоставили, индикатор выполнения по какой-то причине останавливается посередине, даже если прогресс выполнен, поэтому я отредактировал его таким образом:

public void Copy(string sourceDirectory, string targetDirectory)
        {
            DirectoryInfo diSource = new DirectoryInfo(sourceDirectory);
            DirectoryInfo diTarget = new DirectoryInfo(targetDirectory);
            if (!diSource.Exists) return;
            if (diSource.FullName.Contains("System Volume Information")) return;
            if (diSource.FullName.Contains("$RECYCLE.BIN")) return;

            string[] entries;
            try
            {
                entries = Directory.GetFiles(diSource.FullName, "*");
            }
            catch (UnauthorizedAccessException)
            {
                return;
            }
            int max = 1;
            if (entries != null && entries.Count() != 0)
            {
                max = entries.Count();
            }
            Invoke(new Action(() => progressBar1.Maximum = max));
            Invoke(new Action(() => progressBar1.Step = 1));
            Invoke(new Action(() => progressBar1.Value = 0));
            int percent = (int)(((double)progressBar1.Value / (double)progressBar1.Maximum) * 100);
            progressBar1.CreateGraphics().DrawString(percent.ToString() + "%", new Font("Arial", (float)8.25, FontStyle.Regular), Brushes.Black, new PointF(progressBar1.Width / 2 - 10, progressBar1.Height / 2 - 7));
            backgroundCopy.ReportProgress(0, max);

            CopyAll(diSource, diTarget, entries);
            Cursor.Current = Cursors.Default;
        }