windows ManagedThreadID与操作系统ThreadID的关系

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1279950/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-15 12:59:49  来源:igfitidea点击:

Relationship between ManagedThreadID and Operating System ThreadID

c#.netwindowsmultithreading

提问by Odrade

I'm working on a multi-threaded C# Windows application that makes frequent calls into a native dll. These are blocking calls which can sometimes last quite a long time.

我正在开发一个多线程 C# Windows 应用程序,该应用程序频繁调用本机 dll。这些是阻塞调用,有时会持续很长时间。

In certain situations, I'd like to cancel these blocking calls on some worker threads from the main thread The native API I'm using provides a function for this purpose:

在某些情况下,我想从主线程取消对某些工作线程的这些阻塞调用我正在使用的本机 API 为此目的提供了一个函数:

HRESULT CancelBlockingCall(DWORD ThreadID)

Although the documentation for the CancelBlockingCall() isn't terribly clear, I believe I need to pass the ThreadID for the OS-level thread which is blocking on the call. Based on the return codes I'm getting from CancelBlockingCall(), I realized that Thread.ManagedThreadID is not what I need. I found the following on msdn (see the Note):

尽管 CancelBlockingCall() 的文档不是很清楚,但我相信我需要为阻塞调用的操作系统级线程传递 ThreadID。根据我从 CancelBlockingCall() 得到的返回代码,我意识到 Thread.ManagedThreadID 不是我需要的。我在msdn上找到了以下内容(请参阅注释)

An operating-system ThreadId has no fixed relationship to a managed thread, because an unmanaged host can control the relationship between managed and unmanaged threads. Specifically, a sophisticated host can use the CLR Hosting API to schedule many managed threads against the same operating system thread, or to move a managed thread between different operating system threads.

操作系统 ThreadId 与托管线程没有固定的关系,因为非托管主机可以控制托管线程和非托管线程之间的关系。具体来说,复杂的主机可以使用 CLR 托管 API 来针对同一个操作系统线程调度多个托管线程,或者在不同的操作系统线程之间移动托管线程。

Does this mean that there is no way for me to properly call CancelBlockingCall() for a managed thread? Is it impossible to determine the ThreadId of the OS-level thread in which a managed thread is currently executing?

这是否意味着我无法为托管线程正确调用 CancelBlockingCall()?是否无法确定托管线程当前正在执行的操作系统级线程的 ThreadId?

采纳答案by Thomas Levesque

Does this mean that there is no way for me to properly call CancelBlockingCall() for a managed thread? Is it impossible to determine the ThreadId of the OS-level thread in which a managed thread is currently executing?

这是否意味着我无法为托管线程正确调用 CancelBlockingCall()?是否无法确定托管线程当前正在执行的操作系统级线程的 ThreadId?

As Aidan said, you can use the GetCurrentThreadIDAPI to get the OS thread ID.

正如 Aidan 所说,您可以使用GetCurrentThreadIDAPI 来获取操作系统线程 ID。

To keep track of it across managed threads, you could wrap your API calls in a class where you store the OS Thread ID, so that you can stop it later :

要跨托管线程跟踪它,您可以将 API 调用包装在一个存储 OS 线程 ID 的类中,以便稍后停止它:

public class APITask
{
    private uint _osThreadId;

    public void Run()
    {
        _osThreadId = GetCurrentThreadID();
        API.RunBlockingMethod();
    }

    public void Cancel()
    {
        API.CancelBlockingCall(_osThreadId);
    }
}

回答by Anton Tykhyy

As other people mentioned, you could try p/invoking GetCurrentThreadIdbefore calling the blocking native function and registering that id somewhere, but this is a timebomb — some day your managed thread will get pre-empted and re-scheduled to a different OS-level thread between the two p/invoke calls. The only reliable way I can suggest is to write a tiny unmanaged proxy dll, which will call first GetCurrentThreadId(writing it into an out IntPtrsomeplace where it will be visible to managed code) and then your native blocking function. A callback into managed code instead of out IntPtrmight work too; CLR can hardly re-schedule threads while there are unmanaged frames on the stack.

正如其他人提到的,您可以GetCurrentThreadId在调用阻塞本机函数并在某处注册该 id 之前尝试 p/invoking ,但这是一个定时炸弹 - 总有一天您的托管线程将被抢占并重新调度到不同的操作系统级线程在两个 p/invoke 调用之间。我建议的唯一可靠方法是编写一个很小的非托管代理 dll,它将首先调用GetCurrentThreadId(将其写入out IntPtr托管代码可见的某个地方),然后调用您的本机阻塞函数。回调到托管代码而不是也out IntPtr可能起作用;当堆栈中存在非托管帧时,CLR 几乎无法重新调度线程。

Edit:apparently you're not the first person to have such problems: there are two handy methods in System.Threading.Threadwhich allow one to defuse the timebomb I mentioned and p/invoke GetCurrentThreadId(): Thread.BeginThreadAffinity()and Thread.EndThreadAffinity(). CLR host will not re-schedule a managed thread to a different native thread between these calls. Your code needs to run at a high trust level to call these methods, though.

编辑:显然您不是第一个遇到此类问题的人:有两种方便的方法System.Threading.Thread可以消除我提到的定时炸弹和 p/invoke GetCurrentThreadId():Thread.BeginThreadAffinity()Thread.EndThreadAffinity(). CLR 主机不会在这些调用之间将托管线程重新调度到不同的本机线程。但是,您的代码需要以高信任级别运行才能调用这些方法。

回答by Aidan Ryan

You could try P/Invoking the GetCurrentThreadIDAPI

您可以尝试 P/Invoking GetCurrentThreadIDAPI

回答by Khurram Aziz

How about using Abortable ThreadPool?

使用Abortable ThreadPool怎么样?

From the article;

从文章;

Instead, consider a thread pool implementation that handed you back a cookie of sorts for a queued work item. The pool could then also provide a Cancel method that would take one of these cookies and cancel the associated work item, removing it from the queue or aborting the executing work item as appropriate. Implementing a custom thread pool for this task probably isn't the best idea, but there are other alternatives

相反,请考虑一个线程池实现,它为排队的工作项返回一个 cookie。然后,池还可以提供 Cancel 方法,该方法将采用这些 cookie 之一并取消关联的工作项,将其从队列中删除或根据需要中止正在执行的工作项。为此任务实现自定义线程池可能不是最好的主意,但还有其他选择