无论如何要知道何时销毁池线程(或 ThreadStatic 成员)?
Posted
技术标签:
【中文标题】无论如何要知道何时销毁池线程(或 ThreadStatic 成员)?【英文标题】:Anyway to know when a pooled thread (or ThreadStatic member) is destroyed? 【发布时间】:2012-05-11 02:19:36 【问题描述】:我需要将第三方组件添加到我们的其中一款产品中(这是一种可以 24/7 全天候运行的 Windows 服务)。 3PC 是一个 .net 库,它具有一些用于处理图像的硬核 C++ 可爱。 3PC 要求对其运行的每个线程都调用 Initialize 和 Teardown 例程。 这很好,我们在旧软件中使用它,但是这个产品是用 .Net 线程池编写的,池化的工作人员将使用 3PC。我不知道如何安全地调用 Initialize 和 Teardown 例程。
我得到的最接近的是在初始化 ThreadStatic 成员时调用 3PC Initialize
方法,但是我无法在调用 Initialize
的同一线程上调用 Teardown
。
如果我将Initialize
和Teardown
包装在一个对象中,并在对象Finalize 方法中调用Teardown
,那么Teardown
将由GC 自己的Finalize 线程调用,而不是 thread 对象是 static 的(更不用说不能保证终结器会运行)。
显然我担心线程池在后台管理线程时资源泄漏,我不知道线程是否或何时会被销毁或创建,所以我不知道服务会在一段时间内泄漏多少。
有人有什么想法吗?我错过了什么?还有什么要尝试的吗? 谢谢
更新
问:Teardown 是做什么的?
我假设它“释放一些记忆”,但老实说我不知道。我尝试使用 Reflector 对程序集进行扩展,但它很快从 IL 变成了本机机器代码。我要坚持(第三)党路线,必须这样做。
这绝对是一个子系统拆掉的东西。
此外,几年前,我们在另一个产品中发现了一个围绕此组件的错误。没有为每个线程调用初始化程序,这导致了一些非常罕见的未定义行为。
【问题讨论】:
Initialize 和 Teardown 有什么作用?说服 3PC 库以与线程无关的方式安全运行是最好的。特别是“拆解”有什么作用?它是否总是必须在对图像进行某些/每次操作之后调用,还是更像是子系统关闭/终止的事情? 你可以在线程池上运行的每个工作单元之前和之后调用Initialize
和Teardown
吗?
如果 C# 池线程和内核线程之间没有直接的 1:1 映射,则 C# 线程池的文档将包含有关进行阻塞调用的可怕警告。这些警告不存在,因此池线程是内核线程。
AFAIK,线程池注入和退休算法中没有可用的钩子。所以你不能处理线程退休事件。不幸的是,您似乎必须提供自己的线程管理并重构您的项目才能使用它。
如果不实际拆开这个讨厌的组件来找出它为什么需要这个 Initialize 和 Teardown 的东西,(我的意思是,这个组件已经脱离了它的药物:),那就是创建一个小的线程池,固定数量的线程,在每个线程中创建一个单独的组件实例,在开始时调用 Initialize(),(并且可能永远不会调用 Teardown)。
【参考方案1】:
如果最坏的情况发生了,并且没有更好的解决方案即将出现,您可以使用固定数量的线程(=内核数?)创建自己的线程池。通过在每个线程中创建一个 3PC 实例并调用 Initialize(),您应该没问题。
类似:
using System;
using System.Collections.Generic;
using System.Collections.Concurrent;
using System.Linq;
using System.Text;
using System.Threading;
namespace WindowsPoolApp
public abstract class Task
public EventHandler FonComplete;
public ThreadPool myPool;
protected int param;
public Exception error;
public Task(int inParam, EventHandler OnDone) param = inParam; FonComplete = OnDone;
public abstract void run();
;
public class PoolThread
private
3PC my3PC;
BlockingCollection<Task> FinQueue;
public
PoolThread(BlockingCollection<Task> inQueue)
FinQueue=inQueue;
Task inMess;
public void run()
my3PC = new 3PC();
my3PC.Initialize();
while(true)
inMess=FinQueue.Take();
if(inMess==null)
my3PC.Teardown();
return;
try
inMess.run();
inMess.error = null;
catch (Exception e)
inMess.error = e;
inMess.FonComplete(inMess, null);
;
public class ThreadPool
volatile int FthreadCount;
BlockingCollection<Task> queue;
void startThread()
PoolThread thisPoolThread=new PoolThread(queue);
Thread thisThread=new Thread(new ThreadStart(thisPoolThread.run));
thisThread.Priority = ThreadPriority.BelowNormal;
thisThread.IsBackground = true;
thisThread.Start();
void SetThreadCount(int newCount)
while(FthreadCount<newCount)startThread();;
while(FthreadCount>newCount)
queue.Add(default(Task));
FthreadCount--;
;
public ThreadPool(int initThreads)
queue=new BlockingCollection<Task>();
for(FthreadCount=0;FthreadCount<initThreads;FthreadCount++) startThread();
public int threadCount
getreturn FthreadCount;
set
while (FthreadCount < value)
startThread();
FthreadCount++;
;
while (FthreadCount > value)
queue.Add(default(Task));
FthreadCount--;
public void submit(Task task)
task.myPool=this;
queue.Add(task);
;
要启动它,调用'new ThreadPool(numThreads);',要关闭它,将'threadCount'属性设置为0。
【讨论】:
已应用修复 - 在 ctor 中创建 3PC 的原始代码 - 这将不起作用,因为它随后被创建线程调用。将 ctor 调用移至 run() 方法的顶部。以上是关于无论如何要知道何时销毁池线程(或 ThreadStatic 成员)?的主要内容,如果未能解决你的问题,请参考以下文章