.NET cancelTokenSource と ManualResetEvent を一緒に使用する

1. cancelTokenSource は、非同期操作をキャンセルするために使用される C# のクラスです。1 つ以上の非同期操作をキャンセルするメカニズムを提供します。

CancelTokenSource には、次の主なメソッドが含まれています。

  1. Cancel(): このメソッドは保留中の操作をすべてキャンセルし、OperationCanceledException をスローします。Cancel() メソッドを呼び出したときに保留中の操作がない場合は、効果がありません。
  2. Cancel(boolean): このオーバーロードされたメソッドを使用すると、1 つ以上の特定の保留中の操作をキャンセルできます。Cancel(true) が呼び出されると、保留中の操作はすべてキャンセルされ、OperationCanceledException 例外がスローされます。Cancel(false) が呼び出された場合、操作はキャンセルされません。
  3. IsCancelRequested: このプロパティは、キャンセルが要求されたかどうかを示すブール値を返します。キャンセル操作が要求された場合は true を返し、それ以外の場合は false を返します。
  4. ThrowIfCancelRequested(): このメソッドは、キャンセルが要求されたかどうかを確認します。その場合、OperationCanceledException 例外がスローされます。

CancelTokenSource は通常、CancelToken とともに使用されます。cancelanceToken は、非同期操作中にキャンセルを要求するメカニズムを提供する構造体です。cancelTokenSource は、操作をキャンセルする必要がある場合にキャンセルを要求するために非同期操作に渡す cancelToken を生成できます。

以下は、 cancelTokenSource および cancelToken を使用して非同期操作をキャンセルする方法を示す簡単な例です。

CancellationTokenSource cts = new CancellationTokenSource();  
CancellationToken token = cts.Token;  
  
Task.Run(() =>   
{  
    while (true)  
    {  
        token.ThrowIfCancellationRequested();  
        // 异步操作代码...  
    }  
}, cts.Token);  
  
// 在需要取消操作时调用以下方法:  
cts.Cancel();

この例では、 cancelTokenSource オブジェクト cts と、関連付けられた cancelToken オブジェクト トークンを作成します。CancelToken オブジェクトを Task.Run メソッドに渡して、非同期操作でキャンセルが要求されたかどうかを確認します。キャンセル操作が要求された場合、OperationCanceledException 例外がスローされ、それによって非同期操作がキャンセルされます。操作をキャンセルする必要がある場合は、cts.Cancel() メソッドを呼び出してキャンセル操作を要求します。 

 2. ManualResetEvent は、マルチスレッド プログラミングにおけるスレッド間の通信を制御するために使用される同期オブジェクトです。これにより、1 つ以上のスレッドがイベントの発生を待機し、イベントの発生後に実行を継続できるようになります。

ManualResetEvent を使用する場合、通常は次の手順が必要です。

  1. ManualResetEvent オブジェクトを作成します。初期状態はコンストラクターを通じて指定できます。初期状態が true の場合、スレッドは実行を継続できます。それ以外の場合、スレッドはイベントがトリガーされるまでブロックされます。
  2. イベントの発生を待機する必要があるスレッドで、ManualResetEvent の WaitOne() メソッドを呼び出します。これにより、別のスレッドが ManualResetEvent の Set() メソッドを呼び出してイベントをトリガーするまで、現在のスレッドがブロックされます。
  3. イベントの発生後、別のスレッドが ManualResetEvent の Set() メソッドを呼び出してイベントをトリガーすることができ、これにより待機中のスレッドが実行を継続できるようになります。
  4. ManualResetEvent のステータスを手動でリセットする必要がある場合は、Reset() メソッドを呼び出すことができます。このようにして、待機中のスレッドは、Set() メソッドが再度呼び出されてイベントがトリガーされるまでブロックされ続けます。

つまり、ManualResetEvent はスレッド間の同期を支援し、スレッドが続行する前に別のスレッドがタスクを完了するのを待機できるようにします。

3. 一般的なクラス例

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Runtime.CompilerServices;

namespace Common
{
    /// <summary>
    /// 线程通用类
    /// </summary>
    public class TaskCommand
    {
        //用于取消异步操作
        CancellationTokenSource tokenSource = new CancellationTokenSource();
        //用于在多线程编程中控制线程之间的通信
        ManualResetEvent resetEvent = new ManualResetEvent(true);
        Thread thread = null;

        /// <summary>
        /// 队列对象
        /// </summary>
        private Queue<MeterAsyncQueue> AsyncQueues { get; set; }

        /// <summary>
        /// 并发任务数
        /// </summary>
        private int ParallelTaskCount { get; set; }

        /// <summary>
        /// 并行任务集合
        /// </summary>
        private List<Task> ParallelTasks { get; set; }

        /// <summary>
        /// 是否首次执行任务
        /// </summary>
        private bool IsInitTask { get; set; }

        /// <summary>
        /// 锁
        /// </summary>
        private readonly object _objLock = new object();

        /// <summary>
        /// 获取队列锁
        /// </summary>
        private readonly object _queueLock = new object();

        /// <summary>
        /// 开始任务
        /// </summary>
        public void StartData()
        {
            tokenSource = new CancellationTokenSource();
            resetEvent = new ManualResetEvent(true);

            List<int> Ids = new List<int>();
            for (int i = 0; i < 10000; i++)
            {
                Ids.Add(i);
            }
            thread = new Thread(new ThreadStart(() => StartTask(Ids)));
            thread.Start();
        }

        /// <summary>
        /// 暂停任务
        /// </summary>
        public void OutData()
        {
            //task暂停
            resetEvent.Reset();
        }

        /// <summary>
        /// 继续任务
        /// </summary>
        public void ContinueData()
        {
            //task继续
            resetEvent.Set();
        }

        /// <summary>
        /// 取消任务
        /// </summary>
        public void Cancel()
        {
            //释放对象
            resetEvent.Dispose();
            foreach (var CurrentTask in ParallelTasks)
            {
                if (CurrentTask != null)
                {
                    if (CurrentTask.Status == TaskStatus.Running) { }
                    {
                        //终止task线程
                        tokenSource.Cancel();
                    }
                }
            }
            thread.Abort();
        }

        /// <summary>
        /// 执行数据
        /// </summary>
        /// <param name="Index"></param>
        public void Execute(int Index)
        {
            //阻止当前线程
            resetEvent.WaitOne();

            Console.WriteLine("当前第" + Index + "个线程");

            Thread.Sleep(1000);
        }

        //控制线程并行数量
        public void StartTask(List<int> Ids)
        {
            IsInitTask = true;
            ParallelTasks = new List<Task>();
            AsyncQueues = new Queue<MeterAsyncQueue>();
            //获取并发数
            ParallelTaskCount = 5;
            //初始化异步队列
            InitAsyncQueue(Ids);
            //开始执行队列任务
            HandlingTask();

            Task.WaitAll(new Task[] { Task.WhenAll(ParallelTasks.ToArray()) });
        }

        /// <summary>
        /// 初始化异步队列
        /// </summary>
        private void InitAsyncQueue(List<int> Ids)
        {
            foreach (var item in Ids)
            {
                MeterInfo info = new MeterInfo();
                info.Id = item;
                AsyncQueues.Enqueue(new MeterAsyncQueue()
                {
                    MeterInfoTask = info
                });
            }
        }

        /// <summary>
        /// 开始执行队列任务
        /// </summary>
        private void HandlingTask()
        {
            lock (_objLock)
            {
                if (AsyncQueues.Count <= 0)
                {
                    return;
                }

                var loopCount = GetAvailableTaskCount();
                //并发处理队列
                for (int i = 0; i < loopCount; i++)
                {
                    HandlingQueue();
                }
                IsInitTask = false;
            }
        }

        /// <summary>
        /// 处理队列
        /// </summary>
        private void HandlingQueue()
        {
            CancellationToken token = tokenSource.Token;
            lock (_queueLock)
            {
                if (AsyncQueues.Count > 0)
                {
                    var asyncQueue = AsyncQueues.Dequeue();

                    if (asyncQueue == null) return;
                    var task = Task.Factory.StartNew(() =>
                    {
                        if (token.IsCancellationRequested)
                        {
                            return;
                        }
                        //阻止当前线程
                        resetEvent.WaitOne();
                        //执行任务
                        Execute(asyncQueue.MeterInfoTask.Id);

                    }, token).ContinueWith(t =>
                    {
                        HandlingTask();
                    }, TaskContinuationOptions.OnlyOnRanToCompletion | TaskContinuationOptions.ExecuteSynchronously);
                    ParallelTasks.Add(task);
                }
            }
        }

        /// <summary>
        /// 获取当前有效并行的任务数
        /// </summary>
        /// <returns></returns>
        [MethodImpl(MethodImplOptions.Synchronized)]
        private int GetAvailableTaskCount()
        {
            if (IsInitTask)
                return ParallelTaskCount;
            return 1;
        }
    }

    // <summary>
    /// 并发对象
    /// </summary>
    public class MeterAsyncQueue
    {
        public MeterAsyncQueue()
        {
            MeterInfoTask = new MeterInfo();
        }
 
        public MeterInfo MeterInfoTask { get; set; }
    }

    public class MeterInfo
    {
        public MeterInfo()
        {
 
        }
        public int Id { get; set; }
    }
}

おすすめ

転載: blog.csdn.net/weixin_50478033/article/details/133136410