当用户单击表单上的“X”按钮时终止正在运行的线程

时间:2013-09-06 22:21:23

标签: c# multithreading events formclosing

假设我有一个名为mainFrm的表单和一个名为objectA的对象,它包含正在运行的线程。当这两个中的任何一个发生时,我的程序应该关闭:

(1)。用户点击mainFrm中的“X”按钮(因此引发了mainFrm.FormClosing)

(2)。在objectA中引发一个事件(我们将其命名为“connectionClosed”)

因此,无论如何触发(1)(2),此事件链应始终包括:

  1. 任何objectA底层线程的终止。 (我已经知道怎么做了 使用位于objectA内的代码优雅地终止它们)
  2. 当然,mainFrm正在关闭
  3. 解决这个问题的最佳方法是什么?

    这就是我解决的问题

    不确定为什么这个问题没有得到社区的好评。我认为这是相当清楚和直截了当的。这是我如何解决它,我希望这也有助于更好地定义我的意思。如果您有任何其他询问,请告诉我=)

    在表格类中:

    ...
    this.FormClosed += objectA.kill;
    objectA.connectionClosed +=closeForm;
    ...
    
    private void closeForm(object sender, FormClosedEventArgs e)
    {
        try
        {
            this.Invoke(new MethodInvoker(delegate { this.Close(); }));
        }
        catch { }
    }
    

    在objectA的类中:

    ...
    //connectionClosed is raised in different parts of objectA's   threads code
    connectionClosed += killClient; 
    ...
    
    public void killClient(object sender, FormClosedEventArgs e)
    {
        //event should go past this point just once
        if (!_connectionClosed)
        {
            _connectionClosed = true;
    
            try
            {
                ... //close connection killing all threads
            }
            catch { }
        }
    }
    

    根据开头所述的(1)和(2)用例,这是应该发生的事情(如果我没有记错的话)

    (1)。用户点击“X”按钮 - >表单关闭 - > formclosed被提出 - > objectA.kill方法委托被执行 - >内部线程引发一些connectionClosed事件,这将触发更多的objectA.kill执行,但这不会造成任何损害,这要归功于volatile bool _connectionClosed(当然因为有一个try / catch无论如何都会起作用,但它对我来说更有意义再次执行此类代码) - >线程由objectA.kill的唯一完整执行终止 - > SUCCESS

    (2)。服务器关闭连接或网络错误 - > objectA的内部线程检测到连接错误并引发几个connectionClosed事件 - >不同的线程将尝试执行objectA.kill方法委托。 - >同时,在主窗体中,执行closeForm,关闭窗体 - >这也会触发另一个objectA.kill执行(感谢this.FormClosed + = _client.killClient;) - >再次,这没有任何害处,因为_connectionClosed volatile bool只允许一个线程实际执行代码(第一个引发事件的代码) - >只有完整执行objectA.kill才能正常终止线程 - > SUCCESS

    下一步应该是找到一种更方便的方式,这样connectionClosed可能只会上升一次,我现在就谷歌吧=)

2 个答案:

答案 0 :(得分:1)

对事件调用Close()有什么问题?如果在FormClose上切换布尔值,则可以忽略下一个事件。如果要在关闭表单之前中断用户操作,请仅使用FormClosing。使用FormClosing,您可以阻止关闭论坛。如果没有保存某些数据并且您想询问用户是否要在不保存的情况下关闭表单,则主要使用此选项,否则您取消关闭。

所以不要使用FormClosing,使用FormClosed来清理实例。

答案 1 :(得分:1)

我不确定这可以帮助您解决问题,但您应该参考以下代码。 这是一个使用线程对象更改表单颜色的示例。当事件Form1提升20次时_objectA.DoSomething自动关闭(if(count > 20):您可以将此操作视为您的事件connectionClosed被提升);或者您可以在线程运行时随时关闭Form1,点击Form1的“X”按钮。

public delegate void ObjectADoSomethingEventHandler(object sender, ObjectADoSomethingEventArgs e);
public class ObjectADoSomethingEventArgs : EventArgs
{
    public int Value { get; private set; }
    public ObjectADoSomethingEventArgs(int value)
    {
        Value = value;
    }
}


public class ObjectA
{
    public event ObjectADoSomethingEventHandler DoSomething;
    protected void OnDoSomething(int value)
    {
        if (DoSomething != null)
            DoSomething(this, new ObjectADoSomethingEventArgs(value));
    }

    public event EventHandler Closed;
    protected void OnClosed()
    {
        if (Closed != null)
            Closed(this, new EventArgs());
    }


    private BackgroundWorker _worker;
    public ObjectA()
    {
        _worker = new BackgroundWorker();
        _worker.DoWork += new DoWorkEventHandler(_objectA_DoWork);
        _worker.ProgressChanged += new ProgressChangedEventHandler(_objectA_ProgressChanged);
        _worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(_objectA_RunWorkerCompleted);
        _worker.WorkerReportsProgress = true;
        _worker.WorkerSupportsCancellation = true;
    }

    public void Start()
    {
        _worker.RunWorkerAsync();
    }

    public void Kill()
    {
        if (_worker != null && _worker.IsBusy)
        {
            _worker.CancelAsync();
        }
    }

    private void _objectA_DoWork(object sender, DoWorkEventArgs e)
    {
        int count = 0;
        while (true)
        {
            _worker.ReportProgress(count);
            count++;
            if (count > 20)
            {
                return; // exit thread.
            }

            if (_worker.CancellationPending)
            {
                e.Cancel = true;
                return; // Thread cancelled.
            }
            Thread.Sleep(500);
        }
    }

    private void _objectA_ProgressChanged(object sender, ProgressChangedEventArgs e)
    {
        OnDoSomething(e.ProgressPercentage);
    }

    private void _objectA_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        OnClosed();
    }
}


public partial class Form1 : Form
{
    private ObjectA _objectA;

    public Form1()
    {
        InitializeComponent();
        _objectA = new ObjectA();
        _objectA.DoSomething += _objectA_DoSomething;
        _objectA.Closed += _objectA_Closed;
        _objectA.Start();
    }

    private void Form1_FormClosing(object sender, FormClosingEventArgs e)
    {
        _objectA.Kill();
    }

    private int _red = 128;
    private int _green = 128;
    private int _blue = 128;
    void _objectA_DoSomething(object sender, ObjectADoSomethingEventArgs e)
    {
        _red += 15;
        if (_red > 255) _red = 128;
        _green -= 15;
        if (_green < 0) _green = 128;
        _blue += 15;
        if (_blue > 255) _blue = 128;
        this.BackColor = Color.FromArgb(_red, _green, _blue);
        this.Text = string.Format("Count = {0}", e.Value);
    }

    void _objectA_Closed(object sender, EventArgs e)
    {
        Close();
    }
}