【发布时间】:2015-11-02 18:22:41
【问题描述】:
我正在尝试创建一个日志库,并且在调用应用程序关闭之前一切都很好。当调用应用程序关闭时,任何未完成的线程都会被杀死并且特定的日志会丢失。
到目前为止,应用程序甚至在前 10 个线程完成之前就退出了。我需要有关如何让应用程序等到库创建的所有线程都完成的帮助。
注意: 我得到的要求是这样的。修改只能在“Logging”类中,因为这将是一个库并将提供给最终用户。必须在应用程序关闭期间处理日志记录问题。这就是我现在遇到问题的地方。
另外一种解决方案,如在日志记录类中创建一个事件以触发所有日志记录完成,并要求用户在该事件上调用应用程序退出是可能的,但我试图避免这种情况,因为它给最终用户增加了负担并增加了复杂性用于实施。他们有可能会跳过它,这是我不想要的。我正在寻找一个解决方案,比如用户应该执行 'Logging.AddException(....)' 然后忘记它。
请帮忙。如果您不清楚这个想法,请提供 cmets。
这是您可以放入控制台应用程序的完整代码摘要。 注意:在 CASE 1 和 CASE 2 中寻找 cmets。
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace MultithreadKeepAlive
{
class Program
{
static void Main(string[] args)
{
LogLoadTest();
Logging.AddExceptionEntryAsync(new Exception("Last Exception"));
/*
* USE CASE 1: Enable the below lines and you will see how long it is supposed to take.
* Notice that currentDomain_ProcessExit will not trigger if below gets uncommented
*/
//Console.WriteLine("Main thread wait override");
//Console.ReadLine();
}
static void LogLoadTest()
{
//In real world this will be called from any place of application like startup or just after application shutdown is initiated.
//: NOTICE: Unlike the sample here, this will never be on loop and I am not looking for handling multithreads in this class.
// That responsibility I am planning to assign to Logging class.
// AND ALSO the class Logging is going to be in a seperate signed assembly where user of this class ('Program') should not worry about multithreads.
Task t;
for (int i = 0; i < 40; i++)
{
t = Logging.AddExceptionEntryAsync(new Exception("Hello Exception " + i), "Header info" + i);
}
}
}
public class Logging
{
static List<Task> tasks = new List<Task>();
static AppDomain currentDomain;
static Logging()
{
currentDomain = AppDomain.CurrentDomain;
currentDomain.ProcessExit += currentDomain_ProcessExit;
}
public static async Task AddExceptionEntryAsync(Exception ex, string header = "")
{
Task t = Task.Factory.StartNew(() => AddExceptionEntry(ex, header));
tasks.Add(t);
await t;
}
public static void AddExceptionEntry(Exception ex, string header)
{
/* Exception processing and write to file or DB. This might endup in file locks or
* network or any other cases where it will take delays from 1 sec to 5 minutes. */
Thread.Sleep(new Random().Next(1, 1000));
Console.WriteLine(ex.Message);
}
static void currentDomain_ProcessExit(object sender, EventArgs e)
{
Console.WriteLine("Application shutdown triggerd just now.");
Process.GetCurrentProcess().WaitForExit(); //1st attempt.
//Task.WaitAll(tasks.ToArray()); //2nd attempt
while (tasks.Any(t => !t.IsCompleted)) //3rd attempt.
{
}
/* USE CASE 2: IF WORKING GOOD, THIS WILL BE DISPLAYED IN CONSOLE AS LAST
* MESSAGE OF APPLICATION AND WILL WAIT FOR USER. THIS IS NOT WORKING NOW.*/
Console.WriteLine("All complete"); //this message should show up if this work properly
Console.ReadLine(); //for testing purpose wait for input from user after every thread is complete. Check all 40 threads are in console.
}
}
}
【问题讨论】:
-
用线程函数join()试过了吗?如果退出,您可以为每个正在运行的线程调用该方法。所以程序会一直等到它们完成。
-
为什么这个函数:
AddExceptionEntryAsync(Exception ex, string header)没有标记为async而只是在里面使用了Task.Delay?您是否阅读过Process.Exit上的文档?特别是这部分“所有ProcessExit事件处理程序的总执行时间是有限的,就像所有终结器的总执行时间在进程关闭时是有限的。默认为两秒。非托管主机可以通过使用枚举值调用方法来改变这个执行时间。” -
Ron Beyer, lorenz albert:都试过了。它们适用于应用程序正常运行的正常场景。在这里,当应用程序关闭时,没有任何东西可以阻止它杀死所有线程。我尝试过的线程都会被杀死,并且应用程序在没有写入日志的情况下就存在。
-
是的,由于我强调的原因,这不会因等待任务完成而改变。延长关闭时间的唯一方法是使用非托管主机来增加执行时间,否则您应该在用户决定退出应用程序之前处理此问题。根据您使用的 UI 框架,可能会有要拦截的事件,但不是在控制台应用程序中,当
main停止时应用程序停止,此时应用程序有 2 秒的时间进行清理或运行时将介入为你做。 -
顺便说一句,我认为您应该多阅读一下
async/await模式,您有一些方法标记为async,但它们返回void而不是Task(Avoidasync void)。您还同步调用async方法,然后手动生成任务。这里的实现中有很多危险信号,我认为当这些问题得到修复时,很多核心问题都会得到解决。
标签: c# multithreading frameworks shared-libraries application-shutdown