Как остановить поток для отнимающей много времени функции

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

Я пытаюсь использовать приведенный ниже код, но я не уверен, как прервать Thread1 внутри btnStop, поскольку Thread1 помечен как «не существует в текущем контексте».

Не могли бы вы предложить мне / указать мне правильное направление, как это сделать. Заранее спасибо.

namespace SampleStartStop
{    
    public partial class Form1 : Form
    {        
        public Form1()
        {
            InitializeComponent();            
        }

        private void btnStart_Click(object sender, EventArgs e)
        {
            Thread Thread1 = new Thread(SlowFunction);            
            Thread1.Start();                        
        }

        private void btnStop_Click(object sender, EventArgs e)
        {
            Thread1.Abort();
            MessageBox.Show("Processing canceled");
        }
        public void SlowFunction()
        {
            var end = DateTime.Now + TimeSpan.FromSeconds(10);
            while (DateTime.Now < end)
            { }
            MessageBox.Show("Process finished");
        }

    }
}

Обновление: Привет, KCdod, спасибо за вашу помощь. Когда я объявляю поток только как глобальную переменную, я получаю сообщение "Необработанное исключение типа "System.NullReferenceException" произошло в SampleStartStop.exe".

Здравствуйте Алексей, спасибо за поправку. Спасибо zerkms и Алексею за рассказ об отмене жетонов. Следуя примеру в ссылке, которой вы поделились, я смог написать код ниже. Кажется, это работает, но я хотел бы получить одобрение от вас, экспертов, нужно ли что-то изменить или все в порядке.

Единственное сомнение в отношении текущего кода заключается в том, что если нажата кнопка «Стоп», обработка останавливается, но если я снова нажимаю кнопку «Пуск», ничего не происходит, и мне нужно закрыть и снова открыть приложение, чтобы снова заработала кнопка запуска, это это нормально?

Другое сомнение в части внутри «Слушателя». В примере MSDN они помещают «// Выполнить очистку, если необходимо», так о какой очистке они говорят?

public partial class Form1 : Form
{       
    public Form1()
    {
        InitializeComponent();            
    }
    // Create the token source.
    CancellationTokenSource cts = new CancellationTokenSource();

    private void btnStart_Click(object sender, EventArgs e)
    {
        // Pass the token to the cancelable operation.
        ThreadPool.QueueUserWorkItem(new WaitCallback(SlowFunction), cts.Token);                       
    }

    private void btnStop_Click(object sender, EventArgs e)
    {
        // Request cancellation.
        cts.Cancel();
        // Cancellation should have happened, so call Dispose.
        cts.Dispose();

        MessageBox.Show("Processing canceled");
    }
    public void SlowFunction(object obj)
    {
        CancellationToken token = (CancellationToken)obj;

        var end = DateTime.Now + TimeSpan.FromSeconds(10);
        while (DateTime.Now < end)
        {
            // Thread 2: The listener 
            if (token.IsCancellationRequested)
            {
                // Perform cleanup if necessary. 
                //... 
                // Terminate the operation.                  
                break;
            }
        }
        if (!token.IsCancellationRequested)
        {
            MessageBox.Show("Processing finished");
        }
    }

}

Обновление: Спасибо, Алексей за ваше исправление, я изменил код с учетом ваших предложений, и на этот раз он работает хорошо. код, как показано ниже. У меня только проблема, так как в моем реальном коде функции нужен строковый аргумент для работы, и я не знаю, как вызвать его внутри части «WaitCallback (SlowFunction)» и как определить функцию в коде, так как здесь определяется как «public void SlowFunction (object obj) {...}», а в моей реальной функции это «public void SlowFunction (string str)». Я думаю, что мне нужно задать новый вопрос в этом выпуске.

namespace SampleStartStop
{    
    public partial class Form1 : Form
    {       
        // Create the token source.
        CancellationTokenSource cts = new CancellationTokenSource();

        public Form1()
        {
            InitializeComponent();            
        }

        private void btnStart_Click(object sender, EventArgs e)
        {
            if (cts != null)
            {
                cts.Cancel();
            }           
            // Pass the token to the cancelable operation.
            cts = new CancellationTokenSource();
            ThreadPool.QueueUserWorkItem(new WaitCallback(SlowFunction), cts.Token);                         
        }

        private void btnStop_Click(object sender, EventArgs e)
        {
            if (cts != null)
            {
                cts.Cancel();
                cts = null;
                MessageBox.Show("Processing canceled");
            }   
        }
        public void SlowFunction(object obj)
        {
            CancellationToken token = (CancellationToken)obj;

            var end = DateTime.Now + TimeSpan.FromSeconds(5);
            while (DateTime.Now < end)
            {
                if (token.IsCancellationRequested)
                {                
                    break;
                }
            }
            if (!token.IsCancellationRequested)
            {
                MessageBox.Show("Processing finished");
            }
        }

    }
}

person Sarmeu    schedule 06.01.2015    source источник
comment
Токены отмены --- msdn.microsoft.com/ en-us/library/dd997289(v=vs.110).aspx   -  person zerkms    schedule 06.01.2015


Ответы (2)


Нет хорошего способа завершить поток, который не взаимодействует. Действительно, Thread.Abort сделает это, но ценой возможного оставления неутилизированных объектов и заброшенных примитивов синхронизации, что потенциально может дестабилизировать вашу программу.

Исправьте вашу насущную проблему - переместите Thread1 в качестве члена уровня класса вместо локальной переменной. Вам нужно будет проверить, если он уже установлен/очищен:

public partial class Form1 : Form {
   ...

  Thread thread1 = null;            

  private void btnStart_Click(object sender, EventArgs e)
  {
    if (thread1 != null)
    {
         thread1.Abort();
    }
    thread1 = new Thread(SlowFunction);            
    Thread1.Start();                        
  }

  private void btnStop_Click(object sender, EventArgs e)
  {
    if (thread1 != null)
    {
         thread1.Abort();
         thread1 = null;
         MessageBox.Show("Processing canceled");
    }
}

Будет намного лучше, если вы сможете заставить "медленную функцию" сотрудничать при завершении, то есть периодически проверяя какое-то значение. Проверьте токены отмены для .Net. делать это.

person Alexei Levenkov    schedule 06.01.2015
comment
Здравствуйте Алексей! Большое спасибо за помощь. Не могли бы вы увидеть мое обновление в исходном сообщении. - person Sarmeu; 06.01.2015
comment
@Sarmeu, вы не копировали воссоздание объектов из моего образца, и, следовательно, ваш токен отмены остается отмененным (и даже удаленным) навсегда, вместо того, чтобы начинать заново при каждом запуске потока. - person Alexei Levenkov; 06.01.2015
comment
Привет Алексей, что значит воссоздание объектов? Я протестировал образец, который вы разместили, и работал. Когда вы сказали, что было бы лучше сделать функцию взаимодействующей при отмене операции, я только понял, что должен сделать то же самое, что и в ссылке, которой вы поделились. Тогда, что вы предлагаете, это сочетание вашего образца и моего второго кода? Спасибо еще раз - person Sarmeu; 06.01.2015
comment
@Sarmeu, вы вызываете .Dispose() для токена отмены, но продолжаете использовать его для запуска нового потока вместо того, чтобы сразу устанавливать его в null после вызова dispose. В результате всякий раз, когда следующий поток проверяет, нужно ли его отменить, он немедленно получает отмену. Вы должны создать новый токен, когда вы начинаете новый поток (все еще сохраняя его как переменную-член, так как он понадобится вам позже для отмены). - person Alexei Levenkov; 06.01.2015
comment
Добрый день, Алексей! Большое спасибо за ваши предложения. Пожалуйста, смотрите мое обновление в исходном сообщении. Делая, как вы предложили, это работает хорошо. Но у меня есть еще одна проблема, так как моей настоящей SlowFunction для работы требуется строковый аргумент. Спасибо еще раз - person Sarmeu; 06.01.2015
comment
@Sarmeu Вопрос о хамелеоне обычно плохо подходит для SO. Посмотрите, решена ли ваша первоначальная проблема, и задавайте новые вопросы отдельно. - person Alexei Levenkov; 06.01.2015
comment
Привет Алексей. В яблочко. Я не собирался спрашивать что-то новое, эта проблема возникла, когда я понял, что WaitCallback(SlowFunction) не принимает такие аргументы, как WaitCallback(SlowFunction(MyString)). Я спрошу это в новом отдельном вопросе. Большое спасибо за вашу большую помощь. С Уважением - person Sarmeu; 06.01.2015

Вы можете объявить свой поток Thread Thread1; глобальной переменной. В вашем текущем коде ваша Thread1 область видимости ограничивается btnStart_Click() функцией события.

namespace SampleStartStop
{    
    public partial class Form1 : Form
    {   
        Thread Thread1=null;
        public Form1()
        {
            InitializeComponent();            
        }

        private void btnStart_Click(object sender, EventArgs e)
        {
            Thread1 = new Thread(SlowFunction);            
            Thread1.Start();                        
        }

        private void btnStop_Click(object sender, EventArgs e)
        {
            Thread1.Abort();
            MessageBox.Show("Processing canceled");
        }
        public void SlowFunction()
        {
            var end = DateTime.Now + TimeSpan.FromSeconds(10);
            while (DateTime.Now < end)
            { }
            MessageBox.Show("Process finished");
        }

    }
}

Дополнительно. Прерывание потока не является ХОРОШИМ, но вы можете его использовать.

person Kavindu Dodanduwa    schedule 06.01.2015