ManagedThreadID与操作系统ThreadID之间的关系

Odr*_*ade 7 .net c# windows multithreading

我正在研究一个多线程的C#Windows应用程序,它经常调用本机dll.阻塞调用有时会持续很长时间.

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

HRESULT CancelBlockingCall(DWORD ThreadID)
Run Code Online (Sandbox Code Playgroud)

尽管CancelBlockingCall()的文档并不十分清楚,但我相信我需要为操作系统级别的线程传递ThreadID.基于我从CancelBlockingCall()得到的返回码,我意识到Thread.ManagedThreadID不是我需要的.我在msdn上找到了以下内容(参见注释):

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

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

Ant*_*hyy 14

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

编辑:显然你不是第一个遇到这样问题的人:有两个方便的方法System.Threading.Thread允许我们化解我提到的时间炸弹和p/invoke GetCurrentThreadId():Thread.BeginThreadAffinity()Thread.EndThreadAffinity().CLR主机不会将托管线程重新调度到这些调用之间的其他本机线程.但是,您的代码需要以高信任级别运行才能调用这些方法.


Tho*_*que 4

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

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

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

public class APITask
{
    private uint _osThreadId;

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

    public void Cancel()
    {
        API.CancelBlockingCall(_osThreadId);
    }
}
Run Code Online (Sandbox Code Playgroud)