C# 如何为耗时的函数停止线程

C# 如何为耗时的函数停止线程,c#,multithreading,cancel-button,C#,Multithreading,Cancel Button,我正在尝试做一个windows应用程序,我有一个需要几分钟才能完成任务的功能。我有一个开始按钮和 我想添加一个停止按钮,以便在需要停止时停止函数的处理 我正在尝试下面的代码,但我不确定如何中止btnStop中的Thread1,因为Thread1被标记为不存在 在当前环境下 请你向我建议/指出正确的方向,告诉我怎样做才是一个好方法。提前谢谢 namespace SampleStartStop { public partial class Form1 : Form {

我正在尝试做一个windows应用程序,我有一个需要几分钟才能完成任务的功能。我有一个开始按钮和 我想添加一个停止按钮,以便在需要停止时停止函数的处理

我正在尝试下面的代码,但我不确定如何中止btnStop中的Thread1,因为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");
        }

    }
}
更新: 您好,谢谢您的帮助,当我只将线程声明为全局变量时,我得到了一个未处理的异常类型“System.NullReferenceException” 在SampleStartStop.exe中发生

嗨,阿列克谢,谢谢你的更正。感谢zerkms和Alexei分享关于取消代币的信息。遵循您共享的链接中的示例 我能够写出下面的代码。这似乎是可行的,但如果它需要一些改变或者是好的话,我希望得到你们专家的认可

关于当前代码的唯一疑问是,如果按下停止按钮,它将停止处理,但如果我再次单击开始按钮, 没有发生任何事情,我需要关闭并再次打开应用程序,以便重新开始工作开始按钮,这正常吗

另一个疑问是在听者内部的部分。在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");
        }
    }

}
更新: 谢谢Alexei的更正,我用你的建议修改了代码,这次效果很好。代码如下。我只有一个问题,因为在我的实际代码中,函数需要一个字符串参数才能工作,我不知道如何在部分WaitCallbackSlowFunction中调用它,以及如何在代码中定义函数,因为这里的定义类似于这个public void SlowFunctionobject obj{…}在我的实际函数中,类似于这个公共void SlowFunctionstring 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");
            }
        }

    }
}

你可以声明你的线程,线程1;作为全局变量。在当前代码中,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");
        }

    }
}

-线程中止不好,但您可以使用它。

您可以声明您的线程,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");
        }

    }
}

-线程中止不好,但您可以使用它。

没有好方法终止不合作的线程。实际上,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的执行方法。

没有好的方法终止不合作的线程。实际上,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的操作方法。

取消代币取消代币嗨,Alexei,非常感谢您的帮助。请您在原始帖子中查看我的更新。@Sarmeu您没有从我的示例中复制对象的重新创建,因此您的取消令牌将被取消,甚至永远被处置,而不是在每次线程开始时重新开始。嗨,Alexei,您指的是对象的重新创建吗?我测试了你发布和使用的样本。当你说让功能配合取消操作会更好时,我只知道我必须像你共享的链接中的功能那样做。那么,您建议将您的示例和我的第二个代码混合在一起?谢谢again@Sarmeu您在取消令牌时调用.Dispose,但在新线程启动时继续使用它,而不是在Dispose调用后立即将其设置为null。因此,每当下一个线程检查是否需要取消它时,它立即得到cancel now。您应该在启动新线程时创建新令牌,但仍将其保留为成员变量,因为您以后需要它来取消。@Sarmeu通常不适合这样做。请查看您原来的问题是否已解决,并分别提出新问题。嗨,Alexei,非常感谢您的帮助。请您在原始帖子中查看我的更新。@Sarmeu您没有从我的示例中复制对象的复制,因此您的取消令牌将被取消,甚至永远被处置,而不是在每个示例开始时重新开始
嗨,阿列克谢,你说的物体的再创造是什么意思?我测试了你发布和使用的样本。当你说让功能配合取消操作会更好时,我只知道我必须像你共享的链接中的功能那样做。那么,您建议将您的示例和我的第二个代码混合在一起?谢谢again@Sarmeu您在取消令牌时调用.Dispose,但在新线程启动时继续使用它,而不是在Dispose调用后立即将其设置为null。因此,每当下一个线程检查是否需要取消它时,它立即得到cancel now。您应该在启动新线程时创建新令牌,但仍将其保留为成员变量,因为您以后需要它来取消。@Sarmeu通常不适合这样做。请查看原始问题是否已解决,并分别提出新问题。