I. 概要
Winformなどのプラットフォームの開発ではタイマー機能がよく使われますが、プロジェクトタイマーを書きすぎるとソフトウェアのスタックを引き起こしやすく、実行時間によりソフトウェアがクラッシュしてしまう可能性があります。メモリ オーバーフローが原因である可能性があります。具体的な理由については説明しませんでした。もう 1 つは、ソフトウェアを閉じるときに、この時点ではタイマーのスレッドがまだ実行されているため、ソフトウェアを閉じることができないことがよくあります。これらのタイマーを一つずつ閉じてソフトウェアを終了する必要があるか、直接強制終了プログラムコードを使用する必要があり、非常に面倒ですが、後でタイマーをカプセル化し、イベントサブスクリプションのメカニズムを使用して機能を実現できないか考えています。後で実現するので、以下のコードを動かし始めましょう。
2、機能を実現する
新しい winform プロジェクトを作成し、クラス ScanTimer.cs を追加します。
コード:
using System;
using System.Threading;
/// <summary>
/// 定时器
/// </summary>
public class ScanTimer
{
/// <summary>
/// 定时器回调事件
/// </summary>
public static event Action ScanEvent;
/// <summary>
/// 定时器开关的状态
/// </summary>
public static Action<bool> TimerStatus = null;
/// <summary>
/// 定时器执行的次数
/// </summary>
public static Action<int> TimerExecuteCount = null;
/// <summary>
/// 定时器是否打开
/// </summary>
public static bool IsOpen
{
get
{
if (Timer == null)
return false;
return Timer.Enabled;
}
}
//定时器
private static System.Timers.Timer Timer = null;
//间隔时间
private const int IntervalTime = 2000;
//定时清理控制台日志
private static int OutCount = 0;
//是否初始化
private static bool IsInit = false;
/// <summary>
/// 初始化
/// </summary>
private static void Init()
{
//实例化Timer类,
Timer = new System.Timers.Timer();
//设置间隔时间(毫秒);
Timer.Interval = IntervalTime;
//到达时间的时候执行事件;
Timer.Elapsed += new System.Timers.ElapsedEventHandler(Elapsed);
//设置是执行一次(false)还是一直执行(true);
Timer.AutoReset = true;
IsInit = true;
}
/// <summary>
/// 定时器
/// </summary>
/// <param name="source"></param>
/// <param name="e"></param>
private static void Elapsed(object source, System.Timers.ElapsedEventArgs e)
{
OutCount++;
if (OutCount > 10000)
{
OutCount = 0;
//Console.Clear();
}
//定时器的执行次数
if (TimerExecuteCount != null)
TimerExecuteCount(OutCount);
//执行回调
if (ScanEvent != null)
ScanEvent();
}
/// <summary>
/// 打开定时器
/// </summary>
public static void Start()
{
if (!IsInit) Init();
Timer.Enabled = true;
OutCount = 0;
if (TimerStatus != null)
TimerStatus(true);
}
/// <summary>
/// 关闭定时器
/// </summary>
public static void Stop()
{
Timer.Enabled = false;
OutCount = 0;
if (TimerStatus != null)
TimerStatus(false);
}
/// <summary>
/// 清除所有的事件
/// </summary>
public static void ClearAllEvent()
{
if (ScanEvent == null) return;
Delegate[] dels = ScanEvent.GetInvocationList();
foreach (Delegate del in dels)
{
ScanEvent -= del as Action;
}
Console.WriteLine("[ClearAllEvent]清除定时器所有的事件");
}
/// <summary>
/// 获取定时器任务的个数
/// </summary>
/// <returns></returns>
public static int GetTaskCount()
{
if (ScanEvent == null) return 0;
Delegate[] dels = ScanEvent.GetInvocationList();
return dels.Length;
}
/// <summary>
/// 是否存在某个任务
/// </summary>
/// <param name="taskName"></param>
/// <returns></returns>
public static bool IsExistTask(string taskName)
{
if (ScanEvent == null)
return false;
Delegate[] dels = ScanEvent.GetInvocationList();
foreach (Delegate del in dels)
{
object delObj = del.GetType().GetProperty("Method").GetValue(del, null);
string funcName = (string)delObj.GetType().GetProperty("Name").GetValue(delObj, null);
if (funcName == taskName) return true;
}
return false;
}
private ScanTimer() { }
}
Form1_Load イベントと Form1_FormClosing イベントを Form1 フォームに追加します。
Form1_Load イベントはフォームの開始時に 1 回実行され、ここでタイマーを開始できます。
Form1_FormClosing イベントはフォームが閉じられるときに 1 回実行され、タイマーをオフにすることができます。
以下に示すように:
3. テスト
ScanTimer の ScanEvent フィールドはイベントであり、ここにサブスクリプション メカニズムを追加する必要があります。
public static event Action ScanEvent;
実際、原理も非常に単純です。つまり、ScanTimer クラスにタイマーをカプセル化し、このイベントを数秒ごとに実行すると、このイベントをサブスクライブするすべてのメソッドが実行されます。
ScanTimer クラスを呼び出す前に、まず winform の出力タイプをコンソール アプリケーションに変更します。これは観察に便利です。
コード:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace 定时器
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
ScanTimer.ScanEvent += Timer;
ScanTimer.Start();
}
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
ScanTimer.Stop();
}
private void Timer()
{
Console.WriteLine("定时器");
}
}
}
呼び出しも非常に簡単です。Timer() メソッドを記述し、それを ScanEvent イベントに追加するだけです。実行して効果を確認してみましょう。
さらに、ScanTimer クラスの他のメソッドもカプセル化しました。たとえば、タイマーがまだ実行されているかどうかはわかりません。コードが実行される場合もありますが、コード内でそれを出力する必要は必ずしもありません。 TimerExecuteCount デリゲートを使用してタイマーの実行を確認します。また、TimerStatus デリゲートを使用してタイマーが実行中かどうかを確認することもできます。
コード:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace 定时器
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
ScanTimer.ScanEvent += Timer;
ScanTimer.TimerStatus += TimerStatus;
ScanTimer.TimerExecuteCount += TimerExecuteCount;
ScanTimer.Start();
}
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
ScanTimer.Stop();
}
private void Timer()
{
Console.WriteLine("定时器");
}
private void TimerStatus(bool sw)
{
Console.WriteLine("定时器的状态:{0}", sw);
}
private void TimerExecuteCount(int count)
{
Console.WriteLine("定时器执行次数:{0}", count);
}
}
}
走る:
ここで、2 つのデリゲート TimerExecuteCount と TimerStatus は Action タイプです。他のクラスがこれら 2 つのデリゲートをサブスクライブする必要がない場合は、+= を使用する必要はなく、= 記号を直接使用できます。
ScanTimer.ScanEvent += Timer;
ScanTimer.TimerStatus = TimerStatus;
ScanTimer.TimerExecuteCount = TimerExecuteCount;
仕上げる
この投稿が役に立った場合は、注目して、「いいね」を押してメッセージを残してください
終わり