终止运行长时间查询的线程

5

我有一个线程调用其中的一个方法,现在这个方法执行一个查询,可能需要很长时间,可能需要40分钟左右才能完成。

我想给用户一个选择,让他们能够取消这个操作(即停止线程和停止查询以释放数据库)。

我应该提到,我正在使用 .net 4.5、SQL SERVER DB 和 C# 开发 WPF 应用程序。


1
你应该使用BackgroundWorker。它允许取消操作。 - Nikhil Agrawal
5个回答

4
你应该使用BackgroundWorker,它正是你想要的。
从工具箱中拖放它或在代码后台中创建它。它支持取消、报告进度、通知完成情况以及了解它是否正在运行。
以下是一个示例。
void method(){
        BackgroundWorker worker = new BackgroundWorker();
        worker.RunWorkerCompleted += worker_RunWorkerCompleted;
        worker.ProgressChanged += worker_ProgressChanged;
        worker.DoWork += worker_DoWork;
        worker.WorkerSupportsCancellation = true;
        if(!worker.IsBusy)
        {
            worker.RunWorkerAsync();
        }
}

    void worker_DoWork(object sender, DoWorkEventArgs e)
    {
        //do whatever needs to be done on the other thread here.
        object argument = e.Argument; //if passed argument in RunWorkerAsync().
        object result = new object();
        e.Result = result;
        //after making worker global, you can report progress like so:
        worker.ReportProgress(50); //you can also pass a userState, which can be any object, to show some data already.
    }

    void worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
    {
        //you can update a progress bar in here
        int progress = e.ProgressPercentage;

    }

    void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        //when done
    }

    void CancelTheTask()
    {
        if (worker.IsBusy)
        {
            //make worker global first, but then
            worker.CancelAsync();
        }
    }

一个需要注意的问题:不要在DoWork方法中使用未在其中创建的资源。因此,将需要的内容作为参数传递给后台工作者。由后台工作者创建的东西也不应该设置为全局变量,应该通过结果传递。
在取消操作时,RunWorkCompleted也会被触发。现在查询已经被执行了,所以即使应用程序失去了对它的所有资源,查询仍然在运行。
为了取消它,我们需要知道你是如何执行这个查询的,就像@S.Akbari提到的一样。Entity Framework 6也支持取消操作。
关于这一点,请参考使用Queryable时的这个链接
还有另一个例子在这里 或者在这里可以找到没有使用Entity Framework的解决方案。

这并不是他想要的,这不会取消一个查询。 - H H
1
完全正确,亨克!我没有正确阅读它,我已经编辑了我的答案,并提供了一些解决方案。干得好。 - CularBytes

2

如果后台进程积极配合取消并定期检查它,那么这是一个不错的选择,但如果情况并非如此,您需要采取更激烈的方法。 - Alejandro

2

当您的线程被阻塞在等待查询时,它对停止任何操作都是无用的。

确保查询的SqlConnection可以从您的UI访问并关闭它。放弃该线程,它将终止(带有一个您必须抑制的错误)。


1
如果 UI 线程执行长时间操作,它将无法处理 UI 请求。这也被称为“未响应”。可以像这样使用 ThreadPool:
CancellationTokenSource ct;//instantiate it before ThreadPool.QueueUserWorkItem line
private void operation_Click(object sender, RoutedEventArgs e)
{
   ct = new CancellationTokenSource();
   ThreadPool.QueueUserWorkItem(_ =>

        {
          var result = LongTimeOperation();//set the operation in another thread so that the UI thread is kept responding
          //use the Dispatcher to "return" to the UI thread
          Dispatcher.BeginInvoke(new Action(() =>
          {
             //Use result for example : Label1.Text = result.ToString();
          }));
        });
}

为了让用户有选择地取消操作,请使用 CancellationTokenSource,方法如下:
private void cancel_Click(object sender, RoutedEventArgs e)
{
     if (ct != null)
      {
         ct.Cancel();
         ct= null;
      } 
 }

注意:LongTimeOperation() 中,您必须有一个额外的参数,类型为 CancellationToken

private float LongTimeOperation(CancellationToken ct)
{
   if (ct.IsCancellationRequested)
     return -1;
    ....
    ....
}

这个链接与托管线程中的取消有关,非常有用。


0

这是一个常见的问题。但在 WPFWinForm 中,我倾向于使用 BackGroundWorker。请参见此处


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