从阅读the documentation of the ThreadPool.RegisterWaitForSingleObject
方法来看,目前尚不清楚:
它在等待EventWaitHandle
时阻塞当前线程,然后在线程池线程上委托WaitOrTimerCallback
,或者
它委托线程池线程在等待句柄上等待,然后在同一个线程上执行WaitOrTimerCallback
一旦等待句柄发出信号。
它阻塞当前线程,当发出等待句柄信号时,它会调用当前线程上的WaitOrTimerCallback
。但这将是WaitHandle.WaitOne()
的等效功能。此外,它根本不涉及线程池。
它的三个中有哪一个?
答案 0 :(得分:11)
以上都不是,2)是最接近的。确切的细节非常复杂,大部分代码都隐藏在CLR中,并且它已经在.NET版本中发生了变化。您可以查看当前版本in the CoreCLR source,我会给出10,000英尺的视图。
关键是它不会阻塞,工作由专用的非托管线程完成。在源代码中称为“等待线程”,它使用WaitForMultipleObjects()winapi函数等待所有已注册的等待。如果没有(左)它只是睡觉。如果等待列表发生更改,则可以通过QueueUserApc()唤醒该线程,以便它可以使用更新的列表继续等待。
一旦其中一个等待对象发出信号,它就会使用ThreadPool.QueueUserWorkItem()来调用线程池线程上的回调委托目标。如果executeOnlyOnce
参数为true,则从列表中删除等待句柄。并迅速恢复与WFMO等待。线程永远不会结束。
executeOnlyOnce
参数很重要btw,如果你传递 false 并且使用ManualResetEvent,则会出现欢闹。由MRE的Set()方法触发的线程爆炸是一个有趣的工件,可以观察:)你可以在调试器的Debug>中看到等待线程。 Windows>启用非托管调试时的线程。但它没有一个有趣的名字。
答案 1 :(得分:1)
以下测试代码演示了足以回答您问题的行为。
static bool bQuit = false;
static string LastEntry;
static void Main(string[] args)
{
EventWaitHandle ewh = new EventWaitHandle(false, EventResetMode.AutoReset, "TestEvent");
ThreadPool.QueueUserWorkItem(new WaitCallback(Thread1));
Console.WriteLine("TestEvent created.");
while (!bQuit)
{
Console.WriteLine("Press 1 to signal TestEvent.\nPress 2 to quit.");
switch (LastEntry = Console.ReadLine())
{
case "1":
ewh.Set();
break;
case "2":
bQuit = true;
break;
}
}
ewh.Dispose();
Console.WriteLine("Press Enter to finish exiting.");
Console.ReadLine();
}
static void Thread1(object data)
{
WaitHandle wh = EventWaitHandle.OpenExisting("TestEvent");
RegisteredWaitHandle rwh = ThreadPool.RegisterWaitForSingleObject(
wh, new WaitOrTimerCallback(Thread2), null, Timeout.Infinite, false);
Console.WriteLine("Thread {0} registered another thread to run when TestEvent is signaled.",
Thread.CurrentThread.ManagedThreadId);
while(!bQuit)
{
Console.WriteLine("Thread {0} is running.", Thread.CurrentThread.ManagedThreadId);
Thread.Sleep(2000);
}
rwh.Unregister(wh);
Console.WriteLine("Thread {0} is exiting", Thread.CurrentThread.ManagedThreadId);
}
static void Thread2(object data, bool t)
{
Console.WriteLine("Thread {0} started", Thread.CurrentThread.ManagedThreadId);
while(!bQuit && (LastEntry != Thread.CurrentThread.ManagedThreadId.ToString()))
{
Console.WriteLine("Thread {0} is running. Enter {0} to end it",
Thread.CurrentThread.ManagedThreadId);
Thread.Sleep(2000);
}
Console.WriteLine("Thread {0} is exiting", Thread.CurrentThread.ManagedThreadId);
}
输出结果为:
TestEvent created.
Thread 6 registered another thread to run when TestEvent is signaled.
Thread 6 is running.
Press 1 to signal TestEvent.
Press 2 to quit.
Thread 6 is running.
Thread 6 is running.
1
Press 1 to signal TestEvent.
Press 2 to quit.
Thread 13 started
Thread 13 is running. Enter 13 to end it
Thread 6 is running.
Thread 13 is running. Enter 13 to end it
Thread 6 is running.
Thread 13 is running. Enter 13 to end it
1
Press 1 to signal TestEvent.
Press 2 to quit.
Thread 14 started
Thread 14 is running. Enter 14 to end it
Thread 6 is running.
Thread 13 is running. Enter 13 to end it
Thread 14 is running. Enter 14 to end it
Thread 6 is running.
Thread 13 is running. Enter 13 to end it
Thread 14 is running. Enter 14 to end it
13
Press 1 to signal TestEvent.
Press 2 to quit.
Thread 6 is running.
Thread 13 is exiting
Thread 14 is running. Enter 14 to end it
Thread 6 is running.
Thread 14 is running. Enter 14 to end it
2
Press Enter to finish exiting.
Thread 6 is exiting
Thread 14 is exiting
所以你的问题的答案是,我认为,#2。
答案 2 :(得分:1)
我做了以下测试来回答我的问题。答案是#2。
using System;
using System.Threading;
namespace ThreadPoolRegisterWaitForSingleObject
{
class Program
{
static void Main(string[] args)
{
var allTasksWaitHandle = new AutoResetEvent(false);
Action action = () =>
{
Console.WriteLine($"Long task running on {(Thread.CurrentThread.IsThreadPoolThread ? "thread pool" : "foreground")} thread Id: {Thread.CurrentThread.ManagedThreadId}");
for (int i = 0; i < 1000000; i++)
for (int j = 0; j < 100000000; j++) ;
};
//var result = action.BeginInvoke((state) =>
//{
// Console.WriteLine("Async call back says long thing done.");
//}, null);
var result = action.BeginInvoke(null, null);
Console.WriteLine("Main thread not blocked.");
var registerWaitHandle = ThreadPool.RegisterWaitForSingleObject(result.AsyncWaitHandle, (s, b) =>
{
Console.WriteLine("Main long task finished.");
Console.WriteLine($"WaitOrTimerCallback running on {(Thread.CurrentThread.IsThreadPoolThread ? "thread pool" : "foreground")} thread Id: {Thread.CurrentThread.ManagedThreadId}");
allTasksWaitHandle.Set();
}, null, 5000, true);
allTasksWaitHandle.WaitOne();
Console.WriteLine("All threads done.");
Console.WriteLine("Press any key to exit...");
Console.ReadKey();
}
}
}
此外,来自the documentation of the ThreadPool
class的这个特定句子表明在线程池线程上调用了回调。
使用已注册的等待句柄时,系统线程会监视等待句柄的状态。等待操作完成后,来自线程池的工作线程将执行相应的回调函数。