Solved

Delegates and ASync Callback - How to Cancel

Posted on 2007-04-02
9
1,386 Views
Last Modified: 2008-02-01
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
Comment
Question by:Yeavis
  • 5
  • 4
9 Comments
 
LVL 8

Accepted Solution

by:
Rytmis earned 500 total points
ID: 18838304
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
 
LVL 8

Expert Comment

by:Rytmis
ID: 18838517
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
 

Author Comment

by:Yeavis
ID: 18838691
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
 
LVL 8

Expert Comment

by:Rytmis
ID: 18850046
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
Is Your Active Directory as Secure as You Think?

More than 75% of all records are compromised because of the loss or theft of a privileged credential. Experts have been exploring Active Directory infrastructure to identify key threats and establish best practices for keeping data safe. Attend this month’s webinar to learn more.

 

Author Comment

by:Yeavis
ID: 18850658
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
 
LVL 8

Expert Comment

by:Rytmis
ID: 18850702
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
 
LVL 8

Expert Comment

by:Rytmis
ID: 18851385
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
 

Author Comment

by:Yeavis
ID: 18864143
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
 

Author Comment

by:Yeavis
ID: 18878838
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

Is Your Active Directory as Secure as You Think?

More than 75% of all records are compromised because of the loss or theft of a privileged credential. Experts have been exploring Active Directory infrastructure to identify key threats and establish best practices for keeping data safe. Attend this month’s webinar to learn more.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Extention Methods in C# 3.0 by Ivo Stoykov C# 3.0 offers extension methods. They allow extending existing classes without changing the class's source code or relying on inheritance. These are static methods invoked as instance method. This…
We all know that functional code is the leg that any good program stands on when it comes right down to it, however, if your program lacks a good user interface your product may not have the appeal needed to keep your customers happy. This issue can…
This is used to tweak the memory usage for your computer, it is used for servers more so than workstations but just be careful editing registry settings as it may cause irreversible results. I hold no responsibility for anything you do to the regist…
Sending a Secure fax is easy with eFax Corporate (http://www.enterprise.efax.com). First, just open a new email message. In the To field, type your recipient's fax number @efaxsend.com. You can even send a secure international fax — just include t…

867 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

22 Experts available now in Live!

Get 1:1 Help Now