关闭WinForm应用程序时,一次性对象的生命周期

时间:2014-06-10 11:51:59

标签: c# .net winforms dispose

我有一个连接到数据库的Windows窗体应用程序。所有重型SQL查询都在后台线程中实现,因此它们不会阻塞主线程。其中一些查询需要数据库事务。使用关键字初始化所有Db对象以确保正确处置:

using (conn = OpenConnection())
{
    using (tran = conn.BeginTransaction())
    {
        // Do stuff
    }
}

现在,一些用户是住院病人,他们只是在应用程序在后台线程中有活动事务时关闭应用程序。我们的数据库管理员发现这些事务在数据库上仍然活动了几分钟甚至更长时间,这是一个问题,因为打开事务会锁定数据。

那么,当用户决定关闭应用程序时,这些线程中的线程和一次性对象会发生什么?线程使用Task.StartNew()方法创建。

2 个答案:

答案 0 :(得分:1)

当应用程序关闭时,Task.StartNew线程将在进程本身退出时死亡。您可以做的是挂钩processExit事件,然后等待几秒钟或尝试使用一些广播告诉任务清理自己。

AppDomain.CurrentDomain.ProcessExit += CurrentDomain_ProcessExit;

答案 1 :(得分:0)

任务

Foreground and Background Threads:

  

后台线程不会使托管执行环境保持运行。一旦所有前台线程在托管进程中停止(其中.exe文件是托管程序集),系统将停止所有后台线程并关闭。

这也适用于线程池中的线程,这些线程在启动新任务时默认使用,而不指定其他选项:

Task Schedulers

  

默认任务计划程序基于.NET Framework 4 ThreadPool

Foreground and Background Threads:

  

属于托管线程池的线程(即IsThreadPoolThread属性为true的线程)是后台线程。

关于申请终止的事实

之后,我没有找到任何进一步的信息,在线程终止后发生了什么。我所做的,是简单的测试:

static void Main(string[] args)
{
    Task.Factory.StartNew(Method);
    Task.Delay(1000);
}

static void Method()
{
    try
    {
        while (true) { }
    }
    finally
    {
        File.WriteAllText(@"C:\Polygon\Test.txt", "test");
    }
}

答案是, finally块未执行

解决方案

ThreadAbortException - 不在这里

.NET中有一些功能,如ThreadAbortException,称为线程终止的答案,但是:

  

当公共语言运行库(CLR)在托管可执行文件中的所有前台线程结束后停止后台线程时,它不使用Thread.Abort。

Process.Exited事件 - 不在这里

如果您运行其他进程,可以使用以下代码:

var myProcess = new Process();
myProcess.EnableRaisingEvents = true;
myProcess.Exited += new EventHandler(CleanUp);

但是在当前流程的情况下,这也不起作用。

AppDomain.CurrentDomain.ProcessExit - 工作

然而,Avneesh建议的解决方案适用于当前流程:

static void Main(string[] args)
{
    AppDomain.CurrentDomain.ProcessExit += CleanUp;

    Task.Factory.StartNew(Method);
    Task.Delay(1000);
}

static void Method()
{
    try
    {
        while (true) { }
    }
    finally
    {
        CleanUp(null, EventArgs.Empty);
    }
}

static void CleanUp(object sender, EventArgs args)
{
    File.WriteAllText(@"C:\Polygon\Test.txt", "test");
}

流程终止

问题的答案可以在Terminating a Process文章中找到:rosources会发生什么:

  
      
  • 流程中的任何剩余线程都标记为终止。
  •   
  • 释放该流程分配的所有资源。
  •   
  • 关闭所有内核对象。
  •   
  • 流程代码将从内存中删除。
  •   
  • 设置流程退出代码。
  •   
  • 发出过程对象的信号。
  •   

如何终止进程:

  
      
  • 进程的任何线程都调用ExitProcess函数。请注意,如果进程的主线程返回,则C运行时库(CRT)的某些实现会调用ExitProcess。
  •   
  • 流程的最后一个帖子终止
  •   
  • 任何线程都使用句柄​​句柄调用TerminateProcess函数。
  •   
  • 对于控制台进程,当控制台收到CTRL + C或CTRL + BREAK信号时,默认控制台控制处理程序会调用ExitProcess。
  •   
  • 用户关闭系统或注销。
  •   

数据库连接

Connection是一种资源,因此如果处理终止,则释放其所有资源。我检查了它在实践中的样子。

C#代码:

SqlConnection connection = new SqlConnection("Data Source=.;Initial Catalog=[database_name];Integrated Security=True");
connection.Open();
Debugger.Launch();
connection.Close();
connection.Dispose();

会话查询的T-SQL查询:

SELECT COUNT(*) FROM sys.dm_exec_sessions
WHERE nt_user_name = '[user_name]' AND program_name = '.Net SqlClient Data Provider'

我注意到,对于每个新连接,都会创建新会话。最有趣的是,在明确关闭或处理连接后,该会话未被释放。但是 - 目前在我们感兴趣的领域 - 当应用程序流程终止时,会话被释放