无论如何要知道何时销毁池线程(或 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 要求对其运行的每个线程都调用 InitializeTeardown 例程。 这很好,我们在旧软件中使用它,但是这个产品是用 .Net 线程池编写的,池化的工作人员将使用 3PC。我不知道如何安全地调用 InitializeTeardown 例程。

我得到的最接近的是在初始化 ThreadStatic 成员时调用 3PC Initialize 方法,但是我无法在调用 Initialize 的同一线程上调用 Teardown

如果我将InitializeTeardown 包装在一个对象中,并在对象Finalize 方法中调用Teardown,那么Teardown 将由GC 自己的Finalize 线程调用,而不是 thread 对象是 static 的(更不用说不能保证终结器会运行)。

显然我担心线程池在后台管理线程时资源泄漏,我不知道线程是否或何时会被销毁或创建,所以我不知道服务会在一段时间内泄漏多少。

有人有什么想法吗?我错过了什么?还有什么要尝试的吗? 谢谢

更新

问:Teardown 是做什么的?

我假设它“释放一些记忆”,但老实说我不知道​​。我尝试使用 Reflector 对程序集进行扩展,但它很快从 IL 变成了本机机器代码。我要坚持(第三)党路线,必须这样做。

这绝对是一个子系统拆掉的东西。

此外,几年前,我们在另一个产品中发现了一个围绕此组件的错误。没有为每个线程调用初始化程序,这导致了一些非常罕见的未定义行为

【问题讨论】:

Initialize 和 Teardown 有什么作用?说服 3PC 库以与线程无关的方式安全运行是最好的。特别是“拆解”有什么作用?它是否总是必须在对图像进行某些/每次操作之后调用,还是更像是子系统关闭/终止的事情? 你可以在线程池上运行的每个工作单元之前和之后调用InitializeTeardown 吗? 如果 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 成员)?的主要内容,如果未能解决你的问题,请参考以下文章

线程池最佳线程数量到底要如何配置?

线程池中线程是如何知道自己达到 keepAliveTime 时间,然后销毁的?

如何确定线程池中线程数量

如何优雅地关闭线程池?从源码剖析线程池的正确销毁姿势。

多线程使用线程池实现一个简单线程池

何时在 C# 中使用线程池? [关闭]