为什么当工作线程要退出时我们需要检查IsIoPending? [英] Why we need to check the IsIoPending when the worker thread going to exit?

查看:162
本文介绍了为什么当工作线程要退出时我们需要检查IsIoPending?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

从win32threadpool.cpp中我们知道,在检查20s超时之前退出工作线程之前,根据我的理解,它需要通过IsIoPending()方法检查是否有任何IO待处理:

From the win32threadpool.cpp we know that before the worker thread going to exit by checking the 20s timeout, it need to check if there is any IO pending by method IsIoPending(),based on my understanding:

1,当工作线程要退出时,必须完成工作并返回线程池.

1,When the worker thread going to exit, it must finished it's work and return to the thread pool.

2,基于上述1,应该没有IO等待线程退出.

2,Based on the above 1, there should be no IO pending for the thread going to exit.

所以我的问题是,为什么在线程死亡时我们需要检查待处理的IO?或者,我们如何模拟上述情况的发生?

So my question is why we need to check the IO pending when the thread going to die? Alternative, how we can simulate the above condition happens?

RetryWaitForWork:
if (!WorkerSemaphore->Wait(AppX::IsAppXProcess() ? WorkerTimeoutAppX : WorkerTimeout))
{
    if (!IsIoPending())
    {




while (true)
{
RetryRetire:
    DWORD result = RetiredWorkerSemaphore->Wait(AppX::IsAppXProcess() ? WorkerTimeoutAppX : WorkerTimeout, FALSE);
    _ASSERTE(WAIT_OBJECT_0 == result || WAIT_TIMEOUT == result);

    if (WAIT_OBJECT_0 == result)
    {
        foundWork = true;
        counts = WorkerCounter.GetCleanCounts();
        FireEtwThreadPoolWorkerThreadRetirementStop(counts.NumActive, counts.NumRetired, GetClrInstanceId());
        goto Work;
    }

    if (!IsIoPending())
    {

https://github.com/dotnet/coreclr/blob/master/src/vm/win32threadpool.cpp

推荐答案

如果您搜索IsIoPending(这是我遇到不熟悉的内容时要做的第一件事),再往下一点,它会被调用,并在其前面加上以下注释:

If you do a search for IsIoPending (which is one of the first things I do when encountering something that is unfamiliar to me), you'll see that a bit further down, it is called with the following comment preceding it:

// We can't exit a thread that has pending I/O - we'll "retire" it instead.

这几乎可以回答您的问题.为什么我们需要在允许工作线程退出之前检查其是否有任何I/O待处理?好吧,因为我们无法退出具有待处理I/O的线程.

That very nearly answers your question. Why do we need to check if a worker thread has any I/O pending before we allow it to exit? Well, because we cannot exit a thread that has pending I/O.

我想唯一剩下的问题是为什么不?为什么我们不能退出具有未决I/O的线程?为了对此进行调查,让我们看看IsIoPending实际上是做什么.在文件中进一步搜索,然后找到其内容实施:

The only remaining question is, I suppose, why not? Why can't we exit a thread that has pending I/O? To investigate this, let's see what IsIoPending actually does. Search a bit further down the file, and you find its implementation:

// Returns true if there is pending io on the thread.
BOOL ThreadpoolMgr::IsIoPending()
{
    CONTRACTL
    {
        NOTHROW;         
        MODE_ANY;
        GC_NOTRIGGER;
    }
    CONTRACTL_END;

#ifndef FEATURE_PAL
    int Status;
    ULONG IsIoPending;

    if (g_pufnNtQueryInformationThread)
    {
        Status =(int) (*g_pufnNtQueryInformationThread)(GetCurrentThread(),
                                          ThreadIsIoPending,
                                          &IsIoPending,
                                          sizeof(IsIoPending),
                                          NULL);


        if ((Status < 0) || IsIoPending)
            return TRUE;
        else
            return FALSE;
    }
    return TRUE;
#else
    return FALSE;
#endif // !FEATURE_PAL
}

这里的评论并没有告诉我们很多东西,除了确认该函数已正确命名并且按照我们认为的那样做!但是如何执行呢?

The comment doesn't tell us a whole lot here, other than confirming that the function is adequately named and that it does what we think it does! But what about its implementation?

好吧,您首先注意到的是,大多数有趣的内容都是通过#ifndef FEATURE_PAL条件测试来封锁的.那么什么是FEATURE_PAL? PAL代表 P 平台 A A适应 L ayer,它只是一种标记代码的方法,仅在Windows上可以使用.如果定义了FEATURE_PAL,则说明该框架是针对Windows以外的操作系统 进行编译的,因此需要排除Windows特定的代码.这就是您在这里看到的-定义FEATURE_FAL时,此IsIoPending函数仅返回FALSE.仅当它在Windows之上运行时(未定义> 时),才需要检查I/O是否挂起.这非常有力地表明,上面关于不能退出具有挂起I/O的线程的评论是指Windows操作系统的规则.

Well, first thing you notice is that most of the interesting stuff is cordoned off by an #ifndef FEATURE_PAL conditional test. So what is FEATURE_PAL? PAL stands for Platform Adaptation Layer, and it is just a way of marking code that can work only on Windows. If FEATURE_PAL is defined, then the framework is being compiled for an OS other than Windows, so Windows-specific code needs to be excluded. And that's exactly what you see here—when FEATURE_FAL is defined, this IsIoPending function just returns FALSE. Only when it's running on top of Windows (when FEATURE_PAL is not defined) does it bother to check whether I/O is pending. This pretty strongly suggests that the comment above about not being able to exit a thread that has pending I/O is referring to a rule of the Windows operating system.

如果我们在Windows上运行,会发生什么? (通过全局函数指针间接地)调用Windows API函数

What happens if we're running on Windows? A call is made (indirectly, though a global function pointer) to the Windows API function NtQueryInformationThread. The first parameter passes a handle for the current thread (GetCurrentThread()), the second parameter requests the thread information class ThreadIsIoPending, and the next two parameters allow the function to fill in the ULONG variable IsIoPending (they pass its size and a pointer to it).

如果您尝试阅读NtQueryInformationThread的文档,则会看到它是一个内部功能,建议应用程序进行以下操作:

If you try to read the documentation for NtQueryInformationThread, you'll see that it's an internal function and that applications are advised to:

使用公共功能 GetThreadIOPendingFlag 代替以获得此信息.

Use the public function GetThreadIOPendingFlag instead to obtain this information.

.NET源代码没有遵循此建议,因为直到运行Windows XP SP1和.NET 4(以及编写此代码的可能是较早的版本)之后,才引入该功能(GetThreadIOPendingFlag).在Windows的较低版本上.因此,他们只是调用了所有受支持的Windows版本中都可用的内部函数.

The .NET source code doesn't follow this advice because that function (GetThreadIOPendingFlag) was not introduced until Windows XP SP1, and .NET 4 (and presumably older versions, for which this code was written) needed to run on downlevel versions of Windows. So they just called the internal function that was available on all supported versions of Windows.

无论如何,GetThreadIOPendingFlag的文档几乎确认了它所做的一切,我们怀疑它会做什么:如果线程有任何I/O请求挂起,则它返回true,否则返回false. .NET Framework实现调用的内部函数将返回相同的信息.

Anyway, the documentation for GetThreadIOPendingFlag pretty much confirms it does what we would suspect what it does: it returns true if a thread has any I/O requests pending, or false otherwise. The internal function called by the .NET Framework implementation would return the same information.

现在我想我们回到了最初的问题:线程是否有任何待处理的I/O为何如此重要?为什么在杀死它之前需要检查它?嗯,在Windows中,线程发出的I/O请求与该特定线程密不可分.无法将这个I/O请求的所有权或它的结果数据移到另一个线程.换句话说,用户模式 IRP 不能超过最初创建它们的线程的寿命.如果退出线程,则所有挂起的I/O都会被取消,并且永远不会完成.因此,我们在原始注释中得到了简洁明了的规则:如果线程有未决的I/O,则无法退出该线程(因为这样一来,I/O将永远不会完成,并且将永远丢失).

And now I guess we're back to the original question: why does it matter whether a thread has any pending I/O? Why do we need to check this before we kill it? Well, in Windows, an I/O request issued by a thread is inextricably tied to that particular thread. There is no way to move ownership of this I/O request—or its resulting data—to another thread. Put differently, user-mode IRPs cannot outlive the thread that originally created them. If the thread is exited, all pending I/O will be unceremoniously canceled and will never be completed. Thus, we get the rule stated so succinctly in the original comment: if a thread has pending I/O, it cannot be exited (because then that I/O would never be done and would be lost forever).

GetThreadIOPendingFlag函数(或具有类ThreadIsIoPendingNtQueryInformationThread)仅检查指定线程的活动IRP列表以查看其是否为空.如果没有任何I/O请求待处理,则可以安全地退出线程.

The GetThreadIOPendingFlag function (or NtQueryInformationThread with class ThreadIsIoPending) just checks the active IRP list for the specified thread to see whether or not it is empty. If no I/O requests are pending, then it is safe to exit the thread.

工作线程可能有待处理的I/O请求的原因很多,但最常见的情况是

There are many reasons why a worker thread might have pending I/O requests, but the most common situation would be if an overlapped (asynchronous) I/O request were issued by the thread. In this case, the timeout could elapse before I/O completion was signaled. Asynchronous I/O being dependent on the issuing thread is a fundamental limitation of the Win32 architecture, and the .NET Framework's implementation of the thread pool is aware of this limitation and accounts for it.

此检查通常 可能返回false,但是最好只是为了安全起见而对其进行明确检查.这些是标准的防御性编程实践,这是一种框架的重要实践,该框架已在全球范围内交付,并在各种各样的不同条件下运行,并且需要尽可能地健壮.

It may be that this check typically returns false, but it is a good idea to check it explicitly just to be on the safe side. These are standard defensive programming practices—a very important practice for a framework that is shipped worldwide, runs under a vast array of different conditions, and needs to be as robust as possible.

这篇关于为什么当工作线程要退出时我们需要检查IsIoPending?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆