将线程存储在静态变量中是可以的
本文关键字:变量 线程 存储 静态 | 更新日期: 2023-09-27 18:29:30
我想确保我总是只创建一个线程的实例,所以我构建了这个:
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等模式。