Solved

Delegates and ASync Callback - How to Cancel

Posted on 2007-04-02
9
1,384 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
Find Ransomware Secrets With All-Source Analysis

Ransomware has become a major concern for organizations; its prevalence has grown due to past successes achieved by threat actors. While each ransomware variant is different, we’ve seen some common tactics and trends used among the authors of the malware.

 

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

How your wiki can always stay up-to-date

Quip doubles as a “living” wiki and a project management tool that evolves with your organization. As you finish projects in Quip, the work remains, easily accessible to all team members, new and old.
- Increase transparency
- Onboard new hires faster
- Access from mobile/offline

Join & Write a Comment

Article by: Najam
Having new technologies does not mean they will completely replace old components.  Recently I had to create WCF that will be called by VB6 component.  Here I will describe what steps one should follow while doing so, please feel free to post any qu…
It was really hard time for me to get the understanding of Delegates in C#. I went through many websites and articles but I found them very clumsy. After going through those sites, I noted down the points in a easy way so here I am sharing that unde…
Excel styles will make formatting consistent and let you apply and change formatting faster. In this tutorial, you'll learn how to use Excel's built-in styles, how to modify styles, and how to create your own. You'll also learn how to use your custo…
Get a first impression of how PRTG looks and learn how it works.   This video is a short introduction to PRTG, as an initial overview or as a quick start for new PRTG users.

743 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

10 Experts available now in Live!

Get 1:1 Help Now