Abort a thread which is running a long query

2019-01-28 02:35发布

I have a thread which calls one of the methods, now this method executes a query which can take a very long time possibly 40 minutes or so to complete,

I want to give user a a choice to be able to cancel this operation (meaning stop the thread and stop the query to release database).

I should mention that I am developing WPF Application using .net 4.5, SQL SERVER DB and C#.

5条回答
Bombasti
2楼-- · 2019-01-28 02:40

If the UI thread is doing a Long-time operation it won't be able to process UI requests. This is also known as Not Responding. Use ThreadPool like this:

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();
          }));
        });
}

To give user a choice to be able to cancel the operation use CancellationTokenSource like this:

private void cancel_Click(object sender, RoutedEventArgs e)
{
     if (ct != null)
      {
         ct.Cancel();
         ct= null;
      } 
 }

Note: in LongTimeOperation() you must have one more parameter of type CancellationToken

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

This link is useful about Cancellation in Managed Threads.

查看更多
三岁会撩人
3楼-- · 2019-01-28 02:41

When you have your Thread blocked on waiting for the query, it's useless for stopping anything.

Make sure the SqlConnection of the query is accessible from your UI and Close it. Abandon the Thread, it will terminate (with an error you've got to suppress).

查看更多
别忘想泡老子
4楼-- · 2019-01-28 02:52

this is a common problem.But in WPF and WinForm, i'd like to use BackGroundWorker. See Here

查看更多
在下西门庆
5楼-- · 2019-01-28 02:57
走好不送
6楼-- · 2019-01-28 02:58

You should use backgroundworker, it is exactly what you want.

Eather drag and drop it from the toolbox or create it in code - behind. It supports Cancellation, reports progress, notifies when complete and know if it is running or not.

Here is an example.

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();
        }
    }

A important things to look at: Never use resources in the DoWork method that are not created inside it. Thus pass things you need in the background worker as Arguments. And things that are created by the backgroundworker should not be set to a global variable ether, pass by result.

When cancelling, RunWorkCompleted will also be fired. Now the query to the database is already being executed, so that is still running, even when your application lost all resources to it.

To cancel that, we would need to know how you execute the query, like @S.Akbari mentioned is one way. Entity Framework 6 also supports cancellation.

For that: check this when using Queryable

here is another example

Or this solution without Entity Framework.

查看更多
登录 后发表回答