c# - RegisterWaitForSingleObject 未定义行为与 ManualResetEvent

标签 c# multithreading

在我的一个项目中,我需要安排一项任务在后台线程中的某个指定时间间隔后完成。我使用 ThreadPool.RegisterWaitForSingleObject 机制来做到这一点。 根据文档,第一个参数可能是 WaitHandle,它有许多子类。起初我更喜欢 ManualResetEvent;我使用这个事件来发出完成时间表和退出的信号。这是我的代码:

public class ScheduleTester
{
    private long m_LastTicks;
    private RegisteredWaitHandle m_RegisterWaitHandle;
    public ManualResetEvent Event;

    public ScheduleTester()
    {
        Event = new ManualResetEvent(false);
        m_RegisterWaitHandle = ThreadPool.RegisterWaitForSingleObject(Event, new WaitOrTimerCallback(WaitOrTimerCallback), null, 500, false);
        m_LastTicks = DateTime.Now.Ticks;
    }

    private void WaitOrTimerCallback(object state, bool timedOut)
    {
        long ticks = DateTime.Now.Ticks;

        if (timedOut)
        {
            Console.WriteLine(string.Format("Timeout : {0} ... Do scheduled job takes 1 seconds long, Thread ID : {1}", (ticks - m_LastTicks) / 10000f, Thread.CurrentThread.GetHashCode()));                                
        }
        else
        {
            Console.WriteLine(string.Format("Signaled....Unregister , Thread ID : {0}", Thread.CurrentThread.GetHashCode()));

            if (null != m_RegisterWaitHandle)
                m_RegisterWaitHandle.Unregister(null);
        }

        m_LastTicks = ticks;
    }
}

static void Main()
{
    ScheduleTester waitFor = new ScheduleTester();
    Console.ReadKey();
    waitFor.Event.Set();
    Console.ReadKey();
}

我期望的是看到很多行通知预定的工作已经完成,直到我按下一个键来发出事件信号并且一行通知预定的完成。 但我看到的是: output1

ManualResetEvent 替换为 AutoResetEvent 时,一切都很好。 output2

最佳答案

是的,这不是非常直观的行为。您可以从调试器的“调试”>“Windows”>“线程”窗口轻松查看潜在问题。您会看到大量 tp 线程都在执行您的 WaitOrTimerCallback() 方法。

阻止这种情况发生的责任在于你。 RWFSO 的executeOnlyOnce 参数非常非常重要。您现在传递了 false,这使得您等待的同步对象只收到一次信号非常重要。就像 AutoResetEvent 一样。

如果您使用 ManualResetEvent,那么您必须传递 true。并将您的回调调整为:

private void WaitOrTimerCallback(object state, bool timedOut) {
    long ticks = DateTime.Now.Ticks;
    if (timedOut) {
        Console.WriteLine(string.Format("Timeout : {0} ... Do scheduled job takes 1 seconds long, Thread ID : {1}", (ticks - m_LastTicks) / 10000f, Thread.CurrentThread.ManagedThreadId));
        ThreadPool.RegisterWaitForSingleObject(Event, new WaitOrTimerCallback(WaitOrTimerCallback), null, 500, true);
    }
    else {
        Console.WriteLine(string.Format("Signaled....Unregister , Thread ID : {0}", Thread.CurrentThread.ManagedThreadId));
        m_RegisterWaitHandle.Unregister(null);
    }
    m_LastTicks = ticks;
}

您现在将看到它在等待间隔检测超时。不确定这是不是故意的。

关于c# - RegisterWaitForSingleObject 未定义行为与 ManualResetEvent,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37482869/

相关文章:

c# - 如何从 C++ 函数库调用 .Net COM+ 对象?

c# - C# 中的线程 Sql 查询

Python GUI 保持卡住等待线程代码完成运行

java - 如何在后台线程中启动服务器并知道服务器在启动时没有抛出异常?

c# - Entity Framework 代码首先两个表之间的关系

c# - 测试本地 TCP 端口是否空闲

python - 使用 keras fit_generator 进行多处理时出现运行时错误

multithreading - WCF Operation.Context 不是线程安全的?

c# - MVVM Navigation Force关闭Xamarin表格

c# - 我如何跟踪 DRY-ly 状态?