Solved

Delegates and ASync Callback - How to Cancel

Posted on 2007-04-02
9
1,395 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
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 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
Industry Leaders: 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!

 
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
 

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

Revamp Your Training Process

Drastically shorten your training time with WalkMe's advanced online training solution that Guides your trainees to action.

Question has a verified solution.

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

This article introduced a TextBox that supports transparent background.   Introduction TextBox is the most widely used control component in GUI design. Most GUI controls do not support transparent background and more or less do not have the…
The article shows the basic steps of integrating an HTML theme template into an ASP.NET MVC project
Nobody understands Phishing better than an anti-spam company. That’s why we are providing Phishing Awareness Training to our customers. According to a report by Verizon, only 3% of targeted users report malicious emails to management. With compan…
Exchange organizations may use the Journaling Agent of the Transport Service to archive messages going through Exchange. However, if the Transport Service is integrated with some email content management application (such as an antispam), the admini…

742 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