• .NET的CancellationTokenSource和ManualResetEvent结合使用


    一、CancellationTokenSource 是 C# 中的一个类,用于取消异步操作。它提供了一种机制,可以取消一个或多个异步操作。

    CancellationTokenSource 包含以下主要方法:

    1. Cancel(): 该方法会取消所有挂起的操作,并引发 OperationCanceledException 异常。如果在调用 Cancel() 方法时没有挂起的操作,则没有任何效果。
    2. Cancel(boolean): 这个重载方法允许您取消一个或多个特定的挂起操作。如果Cancel(true)被调用,那么所有挂起的操作都将被取消,并引发 OperationCanceledException 异常。如果Cancel(false)被调用,那么不会有任何操作被取消。
    3. IsCancellationRequested: 这个属性会返回一个 bool 值,表明是否已经请求取消操作。如果已经请求取消操作,则返回 true;否则返回 false。
    4. ThrowIfCancellationRequested(): 这个方法会检查是否已经请求取消操作。如果是这样,那么会引发 OperationCanceledException 异常。

    CancellationTokenSource 通常与 CancellationToken 一起使用。CancellationToken 是一个结构,它提供了一种机制,可以在异步操作中请求取消操作。CancellationTokenSource 可以生成一个 CancellationToken,这个 token 可以传递给异步操作,以便在需要取消操作时请求取消。

    以下是一个简单的示例,演示如何使用 CancellationTokenSource 和 CancellationToken 来取消一个异步操作:

    1. CancellationTokenSource cts = new CancellationTokenSource();
    2. CancellationToken token = cts.Token;
    3. Task.Run(() =>
    4. {
    5. while (true)
    6. {
    7. token.ThrowIfCancellationRequested();
    8. // 异步操作代码...
    9. }
    10. }, cts.Token);
    11. // 在需要取消操作时调用以下方法:
    12. cts.Cancel();

    在这个示例中,我们创建了一个 CancellationTokenSource 对象 cts 和一个与之关联的 CancellationToken 对象 token。我们将 CancellationToken 对象传递给 Task.Run 方法,以便在异步操作中检查是否请求了取消操作。如果取消操作被请求,则会引发 OperationCanceledException 异常,从而取消异步操作。在需要取消操作时,我们调用 cts.Cancel() 方法来请求取消操作。 

     二、ManualResetEvent是一种同步对象,用于在多线程编程中控制线程之间的通信。它允许一个或多个线程等待某个事件发生,然后在事件发生后继续执行。

    使用ManualResetEvent时,通常需要以下步骤:

    1. 创建一个ManualResetEvent对象。可以通过构造函数来指定初始状态,如果初始状态为真,则线程可以继续执行;否则,线程将被阻塞直到事件被触发。
    2. 在需要等待某个事件发生的线程中,调用ManualResetEvent的WaitOne()方法。这将会阻塞当前线程,直到另一个线程调用ManualResetEvent的Set()方法触发事件。
    3. 在事件发生后,另一个线程可以调用ManualResetEvent的Set()方法来触发事件,这将使得等待的线程可以继续执行。
    4. 如果需要手动重置ManualResetEvent的状态,可以调用Reset()方法。这样,任何等待的线程将会继续被阻塞,直到再次调用Set()方法触发事件。

    总之,ManualResetEvent可以帮助线程间进行同步,使得线程可以等待另一个线程完成某个任务后才继续执行。

    3、通用类示例

    1. using System;
    2. using System.Collections.Generic;
    3. using System.Linq;
    4. using System.Text;
    5. using System.Threading;
    6. using System.Threading.Tasks;
    7. using System.Runtime.CompilerServices;
    8. namespace Common
    9. {
    10. /// <summary>
    11. /// 线程通用类
    12. /// </summary>
    13. public class TaskCommand
    14. {
    15. //用于取消异步操作
    16. CancellationTokenSource tokenSource = new CancellationTokenSource();
    17. //用于在多线程编程中控制线程之间的通信
    18. ManualResetEvent resetEvent = new ManualResetEvent(true);
    19. Thread thread = null;
    20. /// <summary>
    21. /// 队列对象
    22. /// </summary>
    23. private Queue<MeterAsyncQueue> AsyncQueues { get; set; }
    24. /// <summary>
    25. /// 并发任务数
    26. /// </summary>
    27. private int ParallelTaskCount { get; set; }
    28. /// <summary>
    29. /// 并行任务集合
    30. /// </summary>
    31. private List<Task> ParallelTasks { get; set; }
    32. /// <summary>
    33. /// 是否首次执行任务
    34. /// </summary>
    35. private bool IsInitTask { get; set; }
    36. /// <summary>
    37. ///
    38. /// </summary>
    39. private readonly object _objLock = new object();
    40. /// <summary>
    41. /// 获取队列锁
    42. /// </summary>
    43. private readonly object _queueLock = new object();
    44. /// <summary>
    45. /// 开始任务
    46. /// </summary>
    47. public void StartData()
    48. {
    49. tokenSource = new CancellationTokenSource();
    50. resetEvent = new ManualResetEvent(true);
    51. List<int> Ids = new List<int>();
    52. for (int i = 0; i < 10000; i++)
    53. {
    54. Ids.Add(i);
    55. }
    56. thread = new Thread(new ThreadStart(() => StartTask(Ids)));
    57. thread.Start();
    58. }
    59. /// <summary>
    60. /// 暂停任务
    61. /// </summary>
    62. public void OutData()
    63. {
    64. //task暂停
    65. resetEvent.Reset();
    66. }
    67. /// <summary>
    68. /// 继续任务
    69. /// </summary>
    70. public void ContinueData()
    71. {
    72. //task继续
    73. resetEvent.Set();
    74. }
    75. /// <summary>
    76. /// 取消任务
    77. /// </summary>
    78. public void Cancel()
    79. {
    80. //释放对象
    81. resetEvent.Dispose();
    82. foreach (var CurrentTask in ParallelTasks)
    83. {
    84. if (CurrentTask != null)
    85. {
    86. if (CurrentTask.Status == TaskStatus.Running) { }
    87. {
    88. //终止task线程
    89. tokenSource.Cancel();
    90. }
    91. }
    92. }
    93. thread.Abort();
    94. }
    95. /// <summary>
    96. /// 执行数据
    97. /// </summary>
    98. /// <param name="Index"></param>
    99. public void Execute(int Index)
    100. {
    101. //阻止当前线程
    102. resetEvent.WaitOne();
    103. Console.WriteLine("当前第" + Index + "个线程");
    104. Thread.Sleep(1000);
    105. }
    106. //控制线程并行数量
    107. public void StartTask(List<int> Ids)
    108. {
    109. IsInitTask = true;
    110. ParallelTasks = new List<Task>();
    111. AsyncQueues = new Queue<MeterAsyncQueue>();
    112. //获取并发数
    113. ParallelTaskCount = 5;
    114. //初始化异步队列
    115. InitAsyncQueue(Ids);
    116. //开始执行队列任务
    117. HandlingTask();
    118. Task.WaitAll(new Task[] { Task.WhenAll(ParallelTasks.ToArray()) });
    119. }
    120. /// <summary>
    121. /// 初始化异步队列
    122. /// </summary>
    123. private void InitAsyncQueue(List<int> Ids)
    124. {
    125. foreach (var item in Ids)
    126. {
    127. MeterInfo info = new MeterInfo();
    128. info.Id = item;
    129. AsyncQueues.Enqueue(new MeterAsyncQueue()
    130. {
    131. MeterInfoTask = info
    132. });
    133. }
    134. }
    135. /// <summary>
    136. /// 开始执行队列任务
    137. /// </summary>
    138. private void HandlingTask()
    139. {
    140. lock (_objLock)
    141. {
    142. if (AsyncQueues.Count <= 0)
    143. {
    144. return;
    145. }
    146. var loopCount = GetAvailableTaskCount();
    147. //并发处理队列
    148. for (int i = 0; i < loopCount; i++)
    149. {
    150. HandlingQueue();
    151. }
    152. IsInitTask = false;
    153. }
    154. }
    155. /// <summary>
    156. /// 处理队列
    157. /// </summary>
    158. private void HandlingQueue()
    159. {
    160. CancellationToken token = tokenSource.Token;
    161. lock (_queueLock)
    162. {
    163. if (AsyncQueues.Count > 0)
    164. {
    165. var asyncQueue = AsyncQueues.Dequeue();
    166. if (asyncQueue == null) return;
    167. var task = Task.Factory.StartNew(() =>
    168. {
    169. if (token.IsCancellationRequested)
    170. {
    171. return;
    172. }
    173. //阻止当前线程
    174. resetEvent.WaitOne();
    175. //执行任务
    176. Execute(asyncQueue.MeterInfoTask.Id);
    177. }, token).ContinueWith(t =>
    178. {
    179. HandlingTask();
    180. }, TaskContinuationOptions.OnlyOnRanToCompletion | TaskContinuationOptions.ExecuteSynchronously);
    181. ParallelTasks.Add(task);
    182. }
    183. }
    184. }
    185. /// <summary>
    186. /// 获取当前有效并行的任务数
    187. /// </summary>
    188. /// <returns></returns>
    189. [MethodImpl(MethodImplOptions.Synchronized)]
    190. private int GetAvailableTaskCount()
    191. {
    192. if (IsInitTask)
    193. return ParallelTaskCount;
    194. return 1;
    195. }
    196. }
    197. // <summary>
    198. /// 并发对象
    199. /// </summary>
    200. public class MeterAsyncQueue
    201. {
    202. public MeterAsyncQueue()
    203. {
    204. MeterInfoTask = new MeterInfo();
    205. }
    206. public MeterInfo MeterInfoTask { get; set; }
    207. }
    208. public class MeterInfo
    209. {
    210. public MeterInfo()
    211. {
    212. }
    213. public int Id { get; set; }
    214. }
    215. }

  • 相关阅读:
    vue3的vue-router原理
    【教3妹学算法-每日1题】非递增顺序的最小子序列
    动态代理是什么?应用场景在哪?
    C++标准模板(STL)- 类型支持 (属性查询,获取数组类型在指定维度的大小)
    lua源码分享-gc篇(三)流程之创建对象
    c/c++: 如何区分c和c++
    YOLOv5全面解析教程④:目标检测模型精确度评估
    自动驾驶感知算法实战11——多传感器融合感知方案详解
    计组综合练习(期末复习大题部分适用)
    动态链接库--导出类(二)
  • 原文地址:https://blog.csdn.net/weixin_50478033/article/details/133136410