• Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 1416
  • Last Modified:

Delegates and ASync Callback - How to Cancel

Experts,
     I'm having issues cancelling some threads (up to 8 running at the same time). When I first started my project, I didn't give any thought to being able to cancel the threads I create with my application, however I am thinking about it now. Here is my previous post that I used to create my threads: http://www.experts-exchange.com/Programming/Languages/C_Sharp/Q_22424243.html
    If anyone can tell me how I can cancel the threads created (they have an async callback method), I would appreciate it. I've got to give my users the option to stop testing if they messed something up...

Thanks in advance,
Yeavis
0
Yeavis
Asked:
Yeavis
  • 5
  • 4
1 Solution
 
RytmisCommented:
This is the way I've done it:

- GUI starts a background worker thread
- Worker starts
- Worker does some processing, calls SomeCallback on the GUI every now and then
- User clicks on "Cancel button" -- this sets a flag, say "CancelProcessing" on the GUI
- Worker does more processing, calls SomeCallback. One of the arguments for SomeCallback is a SomeEventArgs object with a member variable "Cancel".
- SomeCallback notices that CancelProcessing was set before, and sets Cancel on the SomeEventArgs to true
- Worker notices that the call to SomeCallback changed SomeEventArgs.Cancel to true, cleans up and quits

I'm sure I didn't explain it very well, so please ask if I left something out. :)
0
 
RytmisCommented:
Taking a look at the other thread you've participated in, you seem to be using asynchronous delegate invocations instead of Thread/ThreadStart, so I'm not sure it's even possible to stop execution in that case. :/
0
 
YeavisAuthor Commented:
I found this on MSDN, and it fits with what I'm doing... however I am not sure how to implement the cancel functionality like they did. Any help would be appreciated..  http://msdn2.microsoft.com/en-us/library/bz33kx67.aspx
0
Independent Software Vendors: We Want Your Opinion

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

 
RytmisCommented:
If I understand correctly, what the code in the article you refer to does is it cancels *pending* operations, not *running* operations. As far as I can tell, you can't cancel asynchronous method calls, you'd have to move to full-blown threads that periodically fire an event, and the subscribers to that event can then set a cancel flag.
0
 
YeavisAuthor Commented:
Could you please show me how I would do that? If possible could you use the link I provided with that code? I'm rather new too threading so any help would be appreciated.
0
 
RytmisCommented:
I'll see what I can do after work, but if someone else wishes to shed light on the issue while I'm gone, go right ahead. :)
0
 
RytmisCommented:
Here's a simplistic example with a thread that increments a counter and sleeps for half a second each time. Written for .NET 2.0, so if you're running 1.1 you will have to combine these two partial classes into one class:

MainForm.designer.cs:

namespace Sandbox
{
      partial class MainForm
      {
            /// <summary>
            /// Designer variable used to keep track of non-visual components.
            /// </summary>
            private System.ComponentModel.IContainer components = null;
            
            /// <summary>
            /// Disposes resources used by the form.
            /// </summary>
            /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
            protected override void Dispose(bool disposing)
            {
                  if (disposing) {
                        if (components != null) {
                              components.Dispose();
                        }
                  }
                  base.Dispose(disposing);
            }
            
            /// <summary>
            /// This method is required for Windows Forms designer support.
            /// Do not change the method contents inside the source code editor. The Forms designer might
            /// not be able to load this method if it was changed manually.
            /// </summary>
            private void InitializeComponent()
            {
                  this.progressBar1 = new System.Windows.Forms.ProgressBar();
                  this.button1 = new System.Windows.Forms.Button();
                  this.button2 = new System.Windows.Forms.Button();
                  this.SuspendLayout();
                  //
                  // progressBar1
                  //
                  this.progressBar1.Location = new System.Drawing.Point(26, 37);
                  this.progressBar1.Maximum = 120;
                  this.progressBar1.Name = "progressBar1";
                  this.progressBar1.Size = new System.Drawing.Size(235, 23);
                  this.progressBar1.Step = 1;
                  this.progressBar1.Style = System.Windows.Forms.ProgressBarStyle.Continuous;
                  this.progressBar1.TabIndex = 0;
                  //
                  // button1
                  //
                  this.button1.Location = new System.Drawing.Point(26, 95);
                  this.button1.Name = "button1";
                  this.button1.Size = new System.Drawing.Size(130, 23);
                  this.button1.TabIndex = 1;
                  this.button1.Text = "Start a long operation";
                  this.button1.UseVisualStyleBackColor = true;
                  this.button1.Click += new System.EventHandler(this.Button1Click);
                  //
                  // button2
                  //
                  this.button2.Enabled = false;
                  this.button2.Location = new System.Drawing.Point(186, 95);
                  this.button2.Name = "button2";
                  this.button2.Size = new System.Drawing.Size(75, 23);
                  this.button2.TabIndex = 2;
                  this.button2.Text = "Cancel";
                  this.button2.UseVisualStyleBackColor = true;
                  this.button2.Click += new System.EventHandler(this.Button2Click);
                  //
                  // MainForm
                  //
                  this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
                  this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
                  this.ClientSize = new System.Drawing.Size(292, 273);
                  this.Controls.Add(this.button2);
                  this.Controls.Add(this.button1);
                  this.Controls.Add(this.progressBar1);
                  this.Name = "MainForm";
                  this.Text = "Sandbox";
                  this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.MainFormFormClosing);
                  this.ResumeLayout(false);
            }
            private System.Windows.Forms.Button button2;
            private System.Windows.Forms.Button button1;
            private System.Windows.Forms.ProgressBar progressBar1;
      }
}


MainForm.cs:

using System;
using System.Collections.Generic;
using System.Drawing;
using System.Windows.Forms;
using System.Threading;
using System.Reflection;

namespace Sandbox
{
      /// <summary>
      /// Description of MainForm.
      /// </summary>
      public partial class MainForm : Form
      {
            [STAThread]
            public static void Main(string[] args)
            {
                  Application.EnableVisualStyles();
                  Application.SetCompatibleTextRenderingDefault(false);
                  Application.Run(new MainForm());
            }
            
            private bool opCanceled = false;
            
            public MainForm()
            {
                  //
                  // The InitializeComponent() call is required for Windows Forms designer support.
                  //
                  InitializeComponent();
                  
                  //
                  // TODO: Add constructor code after the InitializeComponent() call.
                  //
            }
            
            public class ThreadEventArgs : EventArgs
            {
                  public int CurrentPos;
                  public bool Cancel;
            }
            
            public delegate void ThreadEventDelegate(ThreadEventArgs args);
            private delegate void ProgressBarSetterDelegate(int value);            
            
            void Button1Click(object sender, EventArgs e)
            {
                  ThreadEvent += new ThreadEventDelegate(UpdateGUI);
                  button2.Enabled = true;
                  new Thread(new ThreadStart(LongOperation)).Start();
            }
            
            void Button2Click(object sender, EventArgs e)
            {
                  this.opCanceled = true;
            }            
            
            void UpdateGUI(ThreadEventArgs args)
            {
                  if(InvokeRequired)
                        Invoke(new ProgressBarSetterDelegate(SetProgressBarValue), args.CurrentPos);
                  else
                        SetProgressBarValue(args.CurrentPos);

                  args.Cancel = this.opCanceled;
            }
            
            void SetProgressBarValue(int value)
            {
                  progressBar1.Value = value;
            }
            
            public event ThreadEventDelegate ThreadEvent;
            
            void LongOperation()
            {
                  for (int i = 1; i <= 120; i++)
                  {
                        ThreadEventArgs args = new ThreadEventArgs();
                        args.CurrentPos = i;
                        
                        if(ThreadEvent != null)
                              ThreadEvent(args);
                        
                        if(args.Cancel)
                              return;
                        
                        Thread.Sleep(500);
                  }
            }
            

            
            void MainFormFormClosing(object sender, FormClosingEventArgs e)
            {                  
                  this.opCanceled = true;
            }
      }
}

The way this works is LongOperation calls the ThreadEvent event, and any delegate subscribed to that event may set the Cancel flag on the ThreadEventArgs parameter. If the flag is set to true after the event has been raised, LongOperation returns.
0
 
YeavisAuthor Commented:
Sorry for the delay... I will try this out and see if I can get it to work in my existing application. Thanks for the help...
0
 
YeavisAuthor Commented:
Okay, I have tried your solution without any success. I probably should have been more clear about this, but I need to pass parameters to the starting thread as I did in the previous posting. Any help would be appreciated...
0

Featured Post

Technology Partners: We Want Your Opinion!

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

  • 5
  • 4
Tackle projects and never again get stuck behind a technical roadblock.
Join Now