我正在处理一个多线程的C#窗口应用程序,它经常调用一个本机dll。这些都是阻塞呼叫,有时会持续相当长的时间。
在某些情况下,我想从主线程取消对某些工作线程的这些阻塞调用,我使用的本机API为此提供了一个函数:
HRESULT CancelBlockingCall(DWORD ThreadID)虽然CancelBlockingCall()的文档不是很清楚,但我认为我需要为阻塞调用的OS级线程传递ThreadID。基于从CancelBlockingCall()获得的返回代码,我意识到Thread.ManagedThreadID不是我所需要的。我在msdn (见注)上发现了以下内容
操作系统ThreadId与托管线程没有固定关系,因为非托管主机可以控制托管线程和非托管线程之间的关系。具体来说,复杂的主机可以使用CLR宿主API对同一个操作系统线程调度许多托管线程,或者在不同的操作系统线程之间移动托管线程。
这是否意味着我无法为托管线程正确调用CancelBlockingCall()?是否不可能确定托管线程当前正在执行的操作系统级别线程的ThreadId?
发布于 2009-08-14 20:27:37
这是否意味着我无法为托管线程正确调用CancelBlockingCall()?是否不可能确定托管线程当前正在执行的操作系统级别线程的ThreadId?
正如Aidan所说,您可以使用GetCurrentThreadID API来获取OS线程ID。
要在托管线程中跟踪它,可以将API调用包装在存储OS线程ID的类中,以便以后可以停止它:
public class APITask
{
private uint _osThreadId;
public void Run()
{
_osThreadId = GetCurrentThreadID();
API.RunBlockingMethod();
}
public void Cancel()
{
API.CancelBlockingCall(_osThreadId);
}
}发布于 2009-08-15 11:28:15
正如其他人所提到的,在调用阻塞的本机函数并在某个地方注册id之前,您可以尝试调用p/ GetCurrentThreadId,但这是一个定时炸弹--总有一天,您的托管线程会被抢占,并在两个p/调用调用之间重新调度到不同的OS级线程。我建议的唯一可靠方法是编写一个很小的非托管代理dll,它将首先调用GetCurrentThreadId (将其写入托管代码可见的某个地方),然后调用您的原生阻塞函数。对托管代码(而不是out IntPtr )的回调也可能有效;当堆栈上有非托管帧时,CLR很难重新调度线程。
编辑:显然,并不是第一个出现这样问题的人:System.Threading.Thread中有两种方便的方法,一种是用来解除我提到的定时炸弹,另一种是p/invoke GetCurrentThreadId():Thread.BeginThreadAffinity()和Thread.EndThreadAffinity()。CLR主机不会在这些调用之间将托管线程重新调度到不同的本机线程。但是,您的代码需要在较高的信任级别上运行才能调用这些方法。
发布于 2009-08-14 20:17:49
您可以尝试调用P/ GetCurrentThreadID API。
https://stackoverflow.com/questions/1279950
复制相似问题