C# 的异步任务中, 如何暂停, 继续,停止任务
namespace taskTest
{using System;using System.Threading;using System.Threading.Tasks;public class MyService{private Task? workTask;private readonly SemaphoreSlim semaphore = new SemaphoreSlim(0, 1); // 初始为 0,Start() 启动时手动放行private readonly CancellationTokenSource cts = new CancellationTokenSource();private volatile bool autoReleaseSemaphore = true; // 控制 ProcessAsync 是否自动 Releasepublic void Start(){Console.WriteLine("Starting service");autoReleaseSemaphore = true;semaphore.Release(); // 初始启动一次workTask = Task.Run(() => TreadJob(cts.Token));Console.WriteLine("Service started, task ID: " + workTask.Id);}public void Pause(){Console.WriteLine("Pausing service...");autoReleaseSemaphore = false; // 禁止 ProcessAsync 自动 Release}public void Resume(){Console.WriteLine("Resuming service...");autoReleaseSemaphore = true; // 允许 ProcessAsync 自动 Releasesemaphore.Release(); // 手动触发一次 Release,唤醒阻塞的 WaitAsync}public void Stop(){Console.WriteLine("Stopping service...");cts.Cancel();}private async Task TreadJob(CancellationToken token){Console.WriteLine("TreadJob started...");try{if (!cts.IsCancellationRequested){Console.WriteLine("Working...");await ProcessAsync(token);}}catch (OperationCanceledException e){Console.WriteLine(e.Message);Console.WriteLine("Service stopped");}}private async Task ProcessAsync(CancellationToken token){for (int i = 0; i < 100; i++){await semaphore.WaitAsync(token); // 等待信号量token.ThrowIfCancellationRequested();await Task.Delay(500, token);if (autoReleaseSemaphore){semaphore.Release(); // 只有当 autoReleaseSemaphore 为 true 时才 Release}else{continue;}Console.WriteLine($"Process.{i}");}}}
}
namespace taskTest
{internal class Program{static void Main(string[] args){MyService service = new();service.Start();// 等待一段时间,确保 ProcessAsync 有机会执行Console.ReadLine();//Thread.Sleep(1500);// 暂停服务service.Pause();Console.WriteLine("暂停了");Console.WriteLine("按下回车启动Resume");Console.ReadLine();Console.WriteLine("ReadLine执行完");service.Resume();Console.ReadLine();service.Stop();Console.ReadLine();}}
}
结果:
在PLC中的应用:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using S7.Net;namespace taskTest
{public class S7ConnServiceImpl : IS7ConnService{public S7ConnServiceImpl(Db95DeviceHis db95DeviceHis){this.db95DeviceHis = db95DeviceHis;}private static readonly object lockObj = new object(); // 创建一个对象作为锁private Db95DeviceHis db95DeviceHis;private bool myIsConnected = false;private int errorTimes = 0;private CancellationTokenSource cts = new();private readonly SemaphoreSlim semaphore = new SemaphoreSlim(0, 1); // 初始为 0,Start() 启动时手动放行private volatile bool autoReleaseSemaphore = true; // 控制 ProcessAsync 是否自动 Releasepublic bool MyIsConnected{get => myIsConnected;set => myIsConnected = value;}private Plc s7Plc = null;public Plc S7Plc => s7Plc;public void ConnPlc(string myIp, int dbNum, int startByte){return;}public void ConnPlc(string myIp){autoReleaseSemaphore = true;semaphore.Release(); // 初始启动一次try{Task.Factory.StartNew(async () =>{while (!cts.IsCancellationRequested){if (s7Plc == null || !MyIsConnected){try{s7Plc = new Plc(CpuType.S71500, myIp, 0, 1);s7Plc.Open();s7Plc.ReadTimeout = 620;s7Plc.WriteTimeout = 620;//ctsRead1 = new();//ctsRead2 = new();MyIsConnected = s7Plc.IsConnected;if (MyIsConnected){Console.WriteLine("PlcSucessConn!");}}catch (PlcException ex){errorTimes++;s7Plc.Close();s7Plc = null;MyIsConnected = false;Console.WriteLine(ex.Message);Console.WriteLine("ErrorsTime:" + errorTimes);await Task.Delay(2000);}}else if (MyIsConnected){try{MyIsConnected = s7Plc.IsConnected;await semaphore.WaitAsync(cts.Token); // 等待信号量cts.Token.ThrowIfCancellationRequested();await Task.Delay(200);errorTimes = 0;if (autoReleaseSemaphore){semaphore.Release(); // 只有当 autoReleaseSemaphore 为 true 时才 Release}else{continue;}var tempDb95DeviceHis =await S7Plc.ReadClassAsync<Db95DeviceHis>(95, 0);lock (lockObj){if (tempDb95DeviceHis != null){CopyProperties(tempDb95DeviceHis, db95DeviceHis);}Console.WriteLine(db95DeviceHis.Kr500Trigger);Console.WriteLine(db95DeviceHis.Kr16Time);}}catch (PlcException ex){errorTimes++;await Task.Delay(2000);Console.WriteLine($"读取时发生错误:{ex.Message}");Console.WriteLine($"读取时发生错误次数:{errorTimes}");s7Plc.Close();MyIsConnected = false;s7Plc = null;}}}},cts.Token,TaskCreationOptions.LongRunning,TaskScheduler.Default);}catch (OperationCanceledException e){Console.WriteLine(e.Message);Console.WriteLine("Service stopped");}}public void Pause(){Console.WriteLine("Pausing service...");autoReleaseSemaphore = false; // 禁止 ProcessAsync 自动 Release}public void Resume(){Console.WriteLine("Resuming service...");autoReleaseSemaphore = true; // 允许 ProcessAsync 自动 Releasesemaphore.Release(); // 手动触发一次 Release,唤醒阻塞的 WaitAsync}public void Stop(){Console.WriteLine("Stopping service...");cts.Cancel();}/// <summary>/// 反射复制属性值/// </summary>/// <param name="source"></param>/// <param name="destination"></param>/// <exception cref="ArgumentNullException"></exception>public void CopyProperties(object source, object destination){if (source == null || destination == null){throw new ArgumentNullException("Either source or destination is null.");}Type sourceType = source.GetType();Type targetType = destination.GetType();foreach (PropertyInfo sourceProperty in sourceType.GetProperties(BindingFlags.Public | BindingFlags.Instance)){PropertyInfo targetProperty = targetType.GetProperty(sourceProperty.Name);if (targetProperty != null&& targetProperty.CanWrite&& sourceProperty.PropertyType == targetProperty.PropertyType){targetProperty.SetValue(destination, sourceProperty.GetValue(source));}}}}
}
public class Db95DeviceHis : INotifyPropertyChanged{public int Ret { get; set; }public float Kr16Time { get; set; }public float Kr500Time { get; set; }public float Dizuo1Time { get; set; }public float Dizuo2Time { get; set; }public float Dizuo3Time { get; set; }public float Kj1Time { get; set; }public float Kj2Time { get; set; }public float Kj3Time { get; set; }public float ZhouTime { get; set; }public float DamoTime { get; set; }public bool Kr16Trigger{get => kr16Trigger;set{if (kr16Trigger != value && value == true){NotifyPropertyChanged();}kr16Trigger = value;}}public bool Kr500Trigger{get => kr500Trigger;set{if (kr500Trigger != value && value == true){NotifyPropertyChanged();}kr500Trigger = value;}}private bool kr16Trigger;private bool kr500Trigger;public virtual void NotifyPropertyChanged([CallerMemberName] string propertyName = ""){PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));}public event PropertyChangedEventHandler? PropertyChanged;}
public interface IS7ConnService{void ConnPlc(string myIp, int dbNum, int startByte);void ConnPlc(string myIp);bool MyIsConnected{get;}Plc S7Plc { get; }}