3

以下のコードで myForm_FormClosing の後に timer_Tick を呼び出す可能性はありますか?

可能性がある場合: myForm_FormClosing の後に timer_Tick が呼び出されるのを避けるために、myForm_FormClosing 内で timer.Stop() を呼び出すだけで十分ですか?

using System;
using System.Windows.Forms;
using System.ComponentModel;

namespace Test
{
    static class Program
    {
        [STAThread]
        static void Main()
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(new MyForm());
        }
    }

    class MyForm : Form
    {
        private IContainer components;
        private Timer timer;

        protected override void Dispose(bool disposing)
        {
            if (disposing && (components != null))
            {
                components.Dispose();
            }
            base.Dispose(disposing);
        }

        public MyForm()
        {
            components = new Container();
            timer = new Timer(components);

            timer.Interval = 50;
            timer.Tick += timer_Tick;
            timer.Enabled = true;

            FormClosing += myForm_FormClosing;
        }

        private void timer_Tick(object sender, EventArgs e)
        {
        }

        private void myForm_FormClosing(object sender, FormClosingEventArgs e)
        {
        }
    }
}

更新:いくつかのヒントを受け取った後(助けてくれてありがとう)、基本的に次のコードを選択して、必要なものを達成しました。myForm_FormClosing が呼び出された後でも timer1_Tick が呼び出される可能性があることに注意してください。このソリューションは、myForm_FormClosing が呼び出された後に実行される timer1_Tick 内のコードを停止するフラグ (私は doWork と呼びます) を導入するだけです。

using System;
using System.Windows.Forms;
using System.ComponentModel;

namespace Test
{
    static class Program
    {
        [STAThread]
        static void Main()
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(new MyForm());
        }
    }

    class MyForm : Form
    {
        private IContainer components;
        private Timer timer;
        private bool  doWork = true;

        protected override void Dispose(bool disposing)
        {
            if (disposing && (components != null))
            {
                components.Dispose();
            }
            base.Dispose(disposing);
        }

        public MyForm()
        {
            components = new Container();
            timer = new Timer(components);

            timer.Interval = 50;
            timer.Tick += timer_Tick;
            timer.Enabled = true;

            FormClosing += myForm_FormClosing;
        }

        private void timer_Tick(object sender, EventArgs e)
        {
            if (doWork)
            {
                //do the work
            }
        }

        private void myForm_FormClosing(object sender, FormClosingEventArgs e)
        {
            doWork = false;
        }
    }
}
4

3 に答える 3

2

はい、可能です。ドキュメントによると...

フォームが閉じられる前に発生します。

フォームが閉じられると、フォームは破棄され、フォームに関連付けられているすべてのリソースが解放されます。

タイマーは、FormClosing イベントの後まで破棄されません。これは、それがどのように発生するかの非常に不自然な例です。FormClosing が呼び出された後、Timer tick でデバッガーのブレークポイントにヒットしたことがわかります。

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Diagnostics;
using System.Threading.Tasks;
using System.Threading;

namespace WindowsFormsApplication1
{
    public partial class Form1 : Form
    {
        private System.Windows.Forms.Timer _time = new System.Windows.Forms.Timer();
        private Task _t1 = null;
        private Task _t2 = null;
        private bool _closingFlag = false;
        public Form1()
        {
            InitializeComponent();

            _time.Interval = 50;
            _time.Tick += (s, e) => { 
                textBox1.Text = DateTime.Now.ToString();
                if (_closingFlag) Debugger.Break();
            };

            _time.Start();
        }

        private void Form1_FormClosing(object sender, FormClosingEventArgs e)
        {
            _closingFlag = true;

            _t1 = Task.Factory.StartNew(() => { Thread.Sleep(1000); });
            _t2 = Task.Factory.StartNew(() => { Thread.Sleep(1000); });

            Task.WaitAll(_t1, _t2);
        }
    }
}
于 2012-08-14T17:46:50.317 に答える
1

タイマーを使用する場合、タイマーSystem.Windows.Forms.Timerからのティックを処理するにはUIスレッドがアイドル状態である必要があるため、私の理解ではそれは不可能です。

独自のスレッドを持つタイマーをとして使用すると発生する可能性がありますSystem.Timers.Timer。この場合、次のような実装を行うことで、あなたが言及することを回避できます。

class MyForm : Form
{
    private System.Timers.Timer timer;

    private Object justForLocking = new Object();
    private Boolean safeToProceed = true;

    [...]

    public MyForm()
    {
        components = new Container();
        timer = new System.Timers.Timer();

        timer.Interval = 50;
        timer.Elapsed += new System.Timers.ElapsedEventHandler(timer_Elapsed);
        timer.Start();

        FormClosing += myForm_FormClosing;
    }

    void timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
    {
         // In case that this code is being executed after
         // the method myForm_FormClosing has adquired the 
         // exclusive lock over our dummy resource,
         // the timer thread will wait until the resource if released.
         // Once is released, our control flag will be set to false
         // and the timer should just return and never execute again.
         lock(justForLocking)
         {
             if (safeToProceed)
             {
                 // Do whatever you want to do at this point
             }
         }
    }

    private void myForm_FormClosing(object sender, FormClosingEventArgs e)
    {
         lock(justForLocking)
         {
             safeToProceed = false;
         }
         timer.Stop();

         // Do something else
    }

    [...]
}

この他のSOの質問には、関連情報があります。

編集:System.Timers.Timer上記のコードは、System.Windows.Forms.Timerの代わりにaが使用されている場合にのみ有効です。

于 2012-08-14T18:20:09.213 に答える
0

私はそうは思いませんが、とにかく timer.stop() を呼び出すのが最善です。必要な場合は、program.cs でタイマー オブジェクトを作成する必要があります。

于 2012-08-14T17:24:08.753 に答える