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

我正在研究一个multithreading的C#Windows应用程序,这个应用程序会频繁调用本地dll。 这些阻止有时可能持续相当长时间的电话。

在某些情况下,我想从主线程中取消一些工作线程的阻塞调用。我使用的本地API提供了一个用于此目的的函数:

HRESULT CancelBlockingCall(DWORD ThreadID) 

虽然CancelBlockingCall()的文档不是很清楚,但我相信我需要传递阻塞在调用上的OS级线程的ThreadID。 根据我从CancelBlockingCall()得到的返回码,我意识到Thread.ManagedThreadID不是我所需要的。 我在msdn上find了以下内容(请参阅注释) :

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

这是否意味着我没有办法正确调用一个托pipe线程的CancelBlockingCall()? 是否无法确定托pipe线程当前正在执行的操作系统级线程的线程ID?

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

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

要通过托管线程跟踪它,可以将您的API调用包装在存储OS线程标识的类中,以便稍后停止:

 public class APITask { private uint _osThreadId; public void Run() { _osThreadId = GetCurrentThreadID(); API.RunBlockingMethod(); } public void Cancel() { API.CancelBlockingCall(_osThreadId); } } 

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

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

你可以尝试P /调用GetCurrentThreadID API

如何使用Abortable ThreadPool ?

从文章;

相反,请考虑一个线程池实现,它将一个排序的cookie传递给排队的工作项目。 池还可以提供一个取消方法,取得这些cookie中的一个,并取消相关的工作项目,将其从队列中移除或中止正在执行的工作项目。 为这个任务实现一个自定义线程池可能不是最好的主意,但还有其他的选择