C#中Thread.Time的使用

本文介绍了一个使用C#的Thread类实现的线程定时任务。通过创建Timer实例,并结合AutoResetEvent进行线程间的同步,可以定期执行指定的任务,如检查邮件接收状态或输出当前时间。

Thread.Time的使用(线程同步处理之一)

这个类主要是开启一个线程,然后实现按照指定的周期,定期的调用指定的某个函数,实现了定期调用一个函数或程序的办法。
比如想让一个后台程序,定期检查是否收到邮件,或者让一个后台线程定期输出当前时间等。
改造了一下官方案例:

代码块

using System;
using System.Threading;

class TimerExample
{
static void Main()
{
AutoResetEvent autoEvent = new AutoResetEvent(false);
/*上面这句:线程等待的类对象线程等待的类对象,前面在多线程中已经提过,作用是让调用autoEvent.waitone()的程序,在调用处停下来
然后,等待调用autoEvent.set(); 的另一个程序,用此语句发来信号,信号的意思是,告诉autoEvent.waitone()你可以往下继续执行了*/

    StatusChecker statusChecker = new StatusChecker(10);////下面定义好的类,这里实例化一个对象,利用构造函数传入值  
    Timer stateTimer = new Timer(new TimerCallback(statusChecker.CheckStatus), autoEvent, 1000, 250);
    //这个函数是单独开辟的线程,是自动按周期执行,直到调用dispose(),否则该线程一直执行下去
    //参数1.TimerCallback委托的对象,2.想要给printTime传入的参数3.开始之前需要等待多久(毫秒单位)3.每次调用的时间间隔
    autoEvent.WaitOne();//当前线程(现在的主线程)先在这里停下来,等待一个信号,信号来了就往下继续执行!
    stateTimer.Change(0, 500);//收到第一次信号,那我改变一下计时的子线程的函数的调用周期,这里是改变上面Timer中设定的250那个周期值
    Console.WriteLine("\n把调用周期改为1秒一次,等待\n");

    autoEvent.WaitOne();//再次收到信号,信号表示子函数计数又一次到10了
    stateTimer.Dispose();//关闭计时线程的一个命令,如果没有这个命令,那么stateTimer调用子函数会一直执行下去,按照指定周期
    Console.WriteLine("\nDestroying timer.,当前线程:" + Thread.CurrentThread.ManagedThreadId);
    //Console.ReadKey();
}

}

class StatusChecker
{
private int invokeCount;
private int maxCount;

public StatusChecker(int count)
{
    invokeCount = 0;
    maxCount = count;
}

// This method is called by the timer delegate.
public void CheckStatus(Object stateInfo)//被调用的,被TimerCallback委托的函数,必须是具有object 形参且必须为void的返回值的
{
    Thread T = Thread.CurrentThread;
    T.Name = "子线程";
    AutoResetEvent autoEvent = (AutoResetEvent)stateInfo;//从委托函数中传来的参数
    Console.WriteLine("{0} Checking status {1,2}.", DateTime.Now.ToString("h:mm:ss.fff"), (++invokeCount).ToString() + "当前线程 ID : " + Thread.CurrentThread.ManagedThreadId + "当前线程 名称:" + Thread.CurrentThread.Name);

    if (invokeCount == maxCount)
    {
        invokeCount = 0;
        autoEvent.Set();////子函数执行到这里开始发送信号,告诉主程序你可以往下执行,但是这个函数是不停的按照周期被调用的,直到调用了dispose();
    }
}

}

脚注

生成一个脚注1.

<think>好的,我现在需要帮助用户理解C#中的`token.WaitHandle.WaitOne`。首先,我要回忆一下CancellationToken的结构。CancellationToken用于取消异步操作,而它的WaitHandle属性可能和同步等待有关。 用户之前问过在async方法中禁止使用Thread.Sleep或Task.Wait的问题,现在这个问题可能涉及到如何正确等待取消信号。我需要确认WaitHandle.WaitOne是否会导致阻塞,以及是否适合在异步代码中使用。 首先,我应该解释token.WaitHandle.WaitOne的作用。它是用来同步等待取消请求的,调用时会阻塞当前线程直到取消被触发或者超时。比如在非异步的代码中,可能用它来轮询取消状态,但如果在异步方法中使用,可能引发类似之前讨论的阻塞问题。 接下来,需要分析为什么在异步代码中使用WaitOne有问题。因为async方法应该非阻塞,而WaitOne是同步等待,会阻塞线程,导致线程池资源浪费,甚至在UI线程中冻结界面。此外,CancellationToken本身提供了IsCancellationRequested属性和ThrowIfCancellationRequested方法,更适合在循环中检查取消状态,而不需要阻塞。 然后,对比同步和异步等待取消的方法。比如在同步代码中,可以使用WaitHandle.WaitOne,而在异步环境中应该使用await Task.Delay配合轮询,或者使用CancellationToken的WaitHandle异步等待方法,比如Task.WaitAsync,这样不会阻塞线程。 还需要给出正确用法的示例,比如在同步方法中使用WaitOne,而在异步方法中使用await和ThrowIfCancellationRequested。同时,强调避免在异步代码中使用阻塞操作,以防止死锁和性能问题。 最后,总结使用WaitHandle.WaitOne的适用场景和替代方案,确保用户理解何时使用以及如何正确地在异步代码中处理取消请求。</think>在 C# 中,`token.WaitHandle.WaitOne` 是 `CancellationToken` 提供的一个同步阻塞方法,用于**等待取消信号触发**。它的行为与异步编程模型存在根本性冲突,需谨慎使用。以下是详细解析: --- ### **一、核心机制** #### **1. `CancellationToken` 的本质** - 用于实现**协作式取消**,向异步或长时间运行的操作发送取消信号。 - `token.WaitHandle` 返回一个 `WaitHandle` 对象(本质是内核事件对象)。 #### **2. `WaitOne` 的作用** - **同步阻塞**:调用 `WaitOne()` 会阻塞当前线程,直到以下情况之一发生: - 取消令牌被触发(`Cancel()` 调用) - 指定超时时间到达 ```csharp CancellationTokenSource cts = new CancellationTokenSource(); CancellationToken token = cts.Token; // 阻塞当前线程,直到取消被触发或超时(1000ms) bool isCancelled = token.WaitHandle.WaitOne(1000); ``` --- ### **二、典型问题** #### **1. 阻塞线程** - **违反异步原则**:在 `async` 方法中使用会阻塞当前线程,破坏异步非阻塞模型。 - **资源浪费**:阻塞线程池线程会导致线程饥饿(尤其在 ASP.NET Core 中)。 #### **2. 死锁风险** - 在 UI 线程调用 `WaitOne` 会导致界面冻结: ```csharp async void Button_Click(object sender, EventArgs e) { // ❌ 阻塞 UI 线程 token.WaitHandle.WaitOne(); await Task.Delay(1000); // 无法及时执行 } ``` #### **3. 与异步取消机制冲突** - `CancellationToken` 设计初衷是通过 `IsCancellationRequested` 属性或 `ThrowIfCancellationRequested()` 实现**非阻塞轮询**,而非强制阻塞。 --- ### **三、正确使用场景** #### **1. 同步代码中的取消等待** - 适用于纯同步方法需要等待取消信号的场景: ```csharp void SyncMethod(CancellationToken token) { while (!token.IsCancellationRequested) { token.WaitHandle.WaitOne(100); // 同步等待 100ms // 执行同步工作... } } ``` #### **2. 混合同步/异步代码的桥梁** - 在旧代码迁移时作为临时过渡方案(但应尽快重构为纯异步)。 --- ### **四、异步代码中的替代方案** #### **1. 轮询 `IsCancellationRequested`** ```csharp async Task DoWorkAsync(CancellationToken token) { while (!token.IsCancellationRequested) { await Task.Delay(100); // 非阻塞等待 // 执行异步工作... } token.ThrowIfCancellationRequested(); } ``` #### **2. 结合 `Task.Delay` 和取消令牌** ```csharp async Task WaitForCancellationAsync(CancellationToken token) { try { await Task.Delay(Timeout.Infinite, token); } catch (TaskCanceledException) { // 取消已触发 } } ``` #### **3. 使用 `Task.WaitAsync` (.NET 6+)** ```csharp async Task ProcessAsync(CancellationToken token) { await someTask.WaitAsync(token); // 优雅地等待任务完成或取消 } ``` --- ### **五、原理对比** | **方法** | **行为** | **适用场景** | **线程开销** | |-------------------------|------------------------------|--------------------|--------------| | `WaitHandle.WaitOne` | 同步阻塞当前线程 | 纯同步代码 | 高(线程被占用) | | `await Task.Delay` | 非阻塞释放线程 | 异步代码 | 低(线程可重用) | | `ThrowIfCancellationRequested` | 立即抛出异常(若已取消) | 取消检查点 | 无 | --- ### **六、性能影响实测** ```csharp // 同步阻塞方式(1000次调用) token.WaitHandle.WaitOne(1); // => 累计占用线程时间约 1秒 // 异步方式(1000次调用) await Task.Delay(1, token); // => 累计占用线程时间趋近于 0 ``` --- ### **七、总结指南** | **场景** | **推荐方案** | **避免方案** | |------------------------------|---------------------------------------|---------------------------| | 异步方法中等待取消 | `await Task.Delay(Timeout.Infinite, token)` | `token.WaitHandle.WaitOne` | | 同步循环中检测取消 | `token.IsCancellationRequested` + `Thread.Sleep` | 无 | | 需要立即响应取消 | `token.ThrowIfCancellationRequested()` | 无 | --- 坚持异步代码的**非阻塞原则**,仅在同步遗留代码中谨慎使用 `WaitHandle.WaitOne`,在新代码中优先使用 `await` + `CancellationToken` 的组合方案。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Unity青子

难题的解决使成本节约,求打赏

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值