将线程存储在静态变量中是可以的



我想确保我总是只创建一个线程的实例,所以我构建了这个:

private static volatile Thread mdmFetchThread = null;
private static object Locker = new object();
public void myMethod(){
    string someParameter = getParameterDynamically();
    lock(Locker)
    {
    // If an mdmFetchThread is already running, we do not start a new one.
    if(mdmFetchThread != null && mdmFetchThread.ThreadState != ThreadState.Stopped)
    {
       // warn...
    }
    else
    {
        mdmFetchThread = new Thread(() => { doStuff(someParameter); });
        mdmFetchThread.Start();
    }
    }
}

这样做可以吗?或者可能存在哪些陷阱?

//编辑:根据下面的位上下文请求:doStuff()正在调用某个外部系统。这个调用可能超时,但我无法指定超时时间。所以我把它称为mdmFetchThread,稍后再做mdmFetchThread.join(20000)。为了避免两次调用外部系统,我创建了静态变量,以便检查当前是否正在进行调用。

将线程存储在静态变量中是可以的(如果每个AppDomain最多需要一个这样的线程)。你可以在静态存储器中存储你想要的任何东西。

条件mdmFetchThread.ThreadState != ThreadState.Stopped是racy。在线程退出前1纳秒,您可能会发现它为false。然后你不小心什么都没做。维护您自己的布尔状态变量并正确同步。放弃volatile,因为它比必要的更复杂。

考虑切换到Task。它更现代。陷阱更少。

考虑使用Lazy<Task>来创建所需的单例行为。

添加错误处理。后台线程中的崩溃会在不通知开发人员错误的情况下终止进程。

一般来说,如果您使用静态来存储状态(如线程),那么在尝试扩展或管理对象的生存期时可能会出现设计缺陷。我通常尽可能避免静态。

另一种选择可能是创建一个只管理单个线程的类,以作为实例执行任务。这个类可能负责将数据传递给线程或管理线程的状态。例如,确保它只运行一次、优雅地停止线程或在线程完成时进行处理。如果你想扩展,那么你只需要为你的类创建多个实例,每个实例都有自己管理的线程。如果您只想要一个实例,那么只需传递一个实例即可。

如果您正在寻找使此实例可用于整个应用程序的方法(这通常是人们在使用静态变量时试图解决的问题),那么请研究使用ServiceContainers和IServiceProvider等模式。

最新更新