C#线程运行和取消按钮,需要能够取消长时间的处理运行。

3
当用户点击“运行”按钮时,应用程序会运行大量代码以生成模型并在图表中显示。运行需要大约1-2分钟的时间。我还有一个“取消”按钮,该按钮在单击“运行”按钮后启用。我正在使用DotSpatial,因此我的按钮位于功能区UI中的插件面板上。运行和取消的单击事件从插件开始,在其中调用后端类的代码运行和单击。
当用户在运行启动后点击取消时,会有延迟,但是取消方法被调用并执行,但运行永远不会停止,最终我们会看到图表显示。所以,我认为我需要一个独立的线程来运行。我对编程比较陌生,之前没有使用过线程。我已经研究了一下并添加了以下代码,但我的线程方法没有运行。这是我的代码:
运行按钮被点击:
这是在顶部:
//check to see if RunModel thread needs to stop or continue
private volatile bool stopRun = false;
private Thread runThread;

然后,这是从点击事件调用的方法:
public void btnRun_testingThread(object sender, EventArgs e)
{
   //create a new thread to run the RunModel
   if (runThread == null)
   { 
       //we don't want to stop this thread
       stopRun = false;

       runThread = new Thread(RunModel);  
       runThread.Start();              <--this isn't doing anything
   }

因此,我认为当代码执行到runThread.Start()时,它会跳转到我的RunModel方法并开始运行代码。但它没有。另外,一旦我将其正确运行,我将想要取消此线程,因此我有以下内容,该内容从取消单击方法中调用:

private void StopRunThread()
{
    if (runThread != null)
    {
        //we want to stop the thread
        stopRun = true;
        //gracefully pause until the thread exits
        runThread.Join();
        runThread = null;
    }
}

这里是RunModel()函数,我会定时检查stopRun变量是否有改变。

public void RunModel()
{
    ...some code.....

    //check to see if cancel was clicked
    if (stopRun)
    {
        ....clean up code....
        return;
    }

    ....some more code....

    //check to see if cancel was clicked
    if (stopRun)
    {
      ....clean up code....
       return;
    }
}

取消按钮的点击方法:

public void btnCancel_Click(Object sender, EventArgs e)
{
    stopRun = true;
    StopRunThread();
    //the model run has been canceled

    ....some code.....
}

任何有助于使线程启动Run方法的帮助?那么,我需要不断检查运行中的volatile bool来清理所有东西,如果它被停止了吗?谢谢!
2个回答

3

我认为你最好看一下BackgroundWorker - 这个工具可以独立运行,但可以监视取消命令。确保在初始化时添加“WorkerSupportCancellation”:

BackgroundWorker backgroundWorker1 = new BackgroundWorker();
backgroundWorker1.DoWork += new DoWorkEventHandler(backgroundWorker1_DoWork); // This does the job ...
backgroundWorker1.WorkerSupportsCancellation = true; // This allows cancellation.

然后,当点击时,您可以开始进行您的流程:
public void btnRun_testingThread(object sender, EventArgs e)
{
   backgroundWorker1.RunWorkerAsync();
}

您的取消按钮可以发出取消请求:
public void btnCancel_Click(Object sender, EventArgs e)
{
    backgroundWorker1.CancelAsync();
}

那么,当您的工作程序执行其任务时,您的工人可以监视此过程...
void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
    for (int i = 0; i < 100; i++)
    {
        if (backgroundWorker1.CancellationPending)
        {
            break;
        }
        else
        {
            // Do whatever you're doing.
        }
    }
    e.Result = backgroundWorker1.CancellationPending ? null : orders;
}

你可以通过添加进度条等方式进一步增强此功能,但这会变得更加复杂,所以我不会在这里详细介绍。

我根本没有处理循环。 (请参见上面对其他答案的评论)。 我只想随时能够取消长时间处理运行。 - Tonia Roddick
你可以尝试将一系列函数转换为循环。创建一个函数集合并通过它们进行迭代。每次移动到下一个函数时,请检查是否已经点击了取消按钮。 - Brad
或者您可以让定时器在第二个线程上运行,每隔一段时间检查取消状态。有办法将非循环转化为循环。 - Brad
我发现这个教程很有用:https://msdn.microsoft.com/zh-cn/library/system.componentmodel.backgroundworker.aspx - ADH

0

考虑到评论中提供的新信息,我认为你错过了调试器中RunModel()方法的启动,因为你对thread.Start()方法的行为做出了错误的假设。

请参阅MSDN的注释,Thread.Start Method

一旦线程处于ThreadState.Running状态,操作系统可以安排其执行。线程从由传递给线程构造函数的ThreadStart或ParameterizedThreadStart委托表示的方法的第一行开始执行。

一个小演示,表明线程启动需要一些时间,对我来说它在38-40毫秒左右开始:

Stopwatch watch = new Stopwatch();
Thread thread = new Thread((ThreadStart)watch.Stop);
thread.Start();
watch.Start();

Thread.Sleep(5000);
double startedAfter = watch.ElapsedMilliseconds;

自从.NET Framework 4.0以来,建议使用TPL任务而不是显式线程,原因如下:

  • 通过传递Task UI线程同步上下文,您可以轻松地与UI线程同步。
  • 您可以使用CancellationToken轻松停止任务。

我甚至无法从“runThread.Start();” 执行 RunModel()。我在调试模式下运行,代码只是执行到那一行然后继续到下一行。所以,我的线程没有起作用。没有循环。运行 IronPython 和统计方面的东西的过程可以工作,但我不太懂,所以我们希望取消可以停止该过程。我认为 Thread 是正确的方法,也许不是吗? - Tonia Roddick
@ToniaRoddick:没错,RunModel()方法可能不会像调试器中thread.Start()调用后的下一行那样立即启动,因为某些线程初始化/上下文分配工作可能会出现一些延迟。只需按F5(在调试模式下运行)并查看它是否停在RunModel()开头的断点处即可。 - sll

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接