我有一个想法,可以解决在.Net中枚举托管线程和跟踪线程祖先(哪个线程创建了哪个其他线程)的问题。
如果可以使用程序员的对象标记Thread对象,那么在创建它们时会自动将其复制到子线程中,可以使用该标记来跟踪何时创建新线程灵感来自unix,其中,当一个进程被分叉时,它继承了打开的文件句柄等。如果有一些数据是1)线程本地或绑定到Thread对象, 2)自动复制到新线程,3)可修改,这将是一个良好的开端。
我猜测我们必须使用反射来访问一些启动链的Thread对象的成员,因为我在线程中看到的大多数可能有用的东西都被锁定了,但这是一个开始。我不确定这种做法有多么明智。
编辑: 我想我会更好地解释我的用例,因为我认为没有人理解。
我知道明确跟踪线程,我之前在代码中已经做了很多。那不是问题。
基本上,我试图实现一个线程组上下文,就像.Net有一个appdomain-context,一个远程上下文[1]和程序集 - 线程组合 - 本地上下文[2]。
对于从公共线程生成的给定线程组,我想将信息与该分组相关联。虽然我明白.Net没有这个概念(否则我没有问题!),它并没有改变.Net中的每个托管线程都由一个且只有一个其他托管线程创建的事实因此,可以用树形结构绘制。
我试图解决的问题是:我有一个API,它有一个上下文对象。这个API调用一个大型的外部代码库来完成实际的工作,并从它的创建线程开始。该外部没有显式获取API上下文对象的副本,但是它需要一个才能对API进行调用。由于它没有对API上下文对象的引用,因此无法进行这些调用。就目前而言,外部库确实需要进行调用,并且这样做会在单个静态字段中查找当前上下文对象,这意味着每个AppDomain只能有一个API实例。我希望解决这个问题。
这个外部库部分不受我的控制,我的API和外部库之间的接口没有显式传递上下文对象。到目前为止,当外部库需要调用API时,它会在我的API中查看静态字段以获取对上下文对象的引用。
问题是,最终的可执行文件每个AppDomain只能有一个我的API会话实例,因为我们使用静态字段将上下文对象传递给外部库(主力)代码。
一种选择是在我的API中创建一个GetContextObject()方法。当API生成线程以运行外部库代码时,它会记住共享静态字典中的该线程。当外部库代码调用GetContextObject()时,它将查找正在运行的线程并返回该线程的正确上下文对象。
如果外部库代码从未创建自己的线程,那么我没有问题,我总是有100%正确的线程到上下文的映射。但是,外部库确实创建了自己的线程,并且没有我的API知道。当API接收到来自这些线程的调用时,它不知道要放弃哪个上下文对象,并且必须猜测 - 如果只注册了一个上下文对象,它会使用它,否则,它会抛出一个例外,它无法告诉你。
如果我可以将数据标记为由该父线程创建的线程继承的线程对象,那么我可以实现此跟踪系统。
此外,外部库不使用线程池。
基本上,我的选择是:
1)重新设计我的API和外部库之间的接口以传入上下文对象,并重新设计外部库以正确传递此上下文对象。涉及到~100万LOC的累计。
1a)禁止外部库直接使用Thread对象,而是要求他们使用我自己的MyApiThread对象,该对象在创建时会将自己添加到我的自定义跟踪机制中。与选项#1相比,需要在外部库中更改更少的代码,但仍需要进行大量的返工。
2)强制我的API的使用者在新的AppDomain中启动每个API会话,以便我可以将我的上下文对象存储在静态字段中(这就是今天的解决方案)。 AppDomains涉及很多开销,我不想强迫我的用户使用。
3)找到一种跟踪线程祖先的方法,以便能够根据调用线程将正确的上下文对象返回到从外部库调用的代码。这是这篇文章的主题。
对于那些说Windows没有子父线程概念的人来说,你不在基础之上 - 这是无关紧要的。 DotNet不是一个仅限Windows的系统,它的设计是将它与运行的机器和操作系统隔离开来,这就是.Net以Mono的形式存在于Linux,Solaris,FreeBSD的原因。此外,Java确实具有我需要的线程祖先的概念,而Java是在Windows上实现的,因此这是一个非常可能和合理的概念。虽然我意识到.Net api有一个特定于微软的特定弯曲,但实现了.Net和Windows在很大程度上是独立的。
答案 0 :(得分:3)
事实上,我会将我的评论作为答案并指向Jeffrey Richter。
CallContext类使您能够存储“逻辑执行路径”的数据,该路径可以跨线程和AppDomains。
答案 1 :(得分:0)
只需为shambulator answer添加更多信息。
CallContext.GetLogicalData和CallContext.SetLogicalData执行技巧