C# .Net - Как заставить приложение ждать, пока все потоки, созданные в библиотеке, не будут завершены
Я пытаюсь создать библиотеку журналов, и все хорошо, пока не будет вызвано завершение работы приложения. Когда вызывается закрытие приложения, любой незавершенный поток уничтожается, а конкретный журнал теряется.
На данный момент приложение закрывается еще до завершения первых 10 потоков. Мне нужна помощь о том, как заставить приложение ждать, пока все потоки, созданные библиотекой, не будут завершены.
ПРИМЕЧАНИЕ: требования, которые я получил, таковы. Модификации должны быть только в классе "Ведение журнала", так как это будет библиотека и будет предоставлена конечным пользователям. Обработка проблем с журналированием во время завершения работы приложения должна выполняться внутри него. Вот где у меня сейчас проблемы.
В качестве альтернативы возможно создание решения, такого как создание события в классе ведения журнала, чтобы инициировать завершение всего ведения журнала, и просьба к пользователю вызвать выход из приложения для этого события, но я стараюсь избегать этого, поскольку оно добавляет это бремя для конечного пользователя и усложняет реализацию. Есть вероятность, что они могут пропустить это, что я не хочу. Я ищу решение, как пользователь должен сделать "Logging.AddException(....)", а затем забыть об этом.
Пожалуйста помоги. Предоставьте комментарии, если вам не ясна идея.
Вот полный реферат кода, который вы можете поместить в консольное приложение.Примечание: ищите комментарии в случае 1 и 2.
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.
}
}
}
3 ответа
На данный момент я сам нашел обходной путь.
/// <summary>
/// Makes the current thread Wait until any of the pending messages/Exceptions/Logs are completly written into respective sources.
/// Call this method before application is shutdown to make sure all logs are saved properly.
/// </summary>
public static void WaitForLogComplete()
{
Task.WaitAll(tasks.Values.ToArray());
}
Ты можешь попробовать
Task.WaitAll(tasks);
Это ожидает завершения всех предоставленных объектов Task.
ОБНОВЛЕНИЕ: использование async / await
С помощью async и await мы формализуем и разъясняем, как начинаются и заканчиваются асинхронные неблокирующие методы. Асинхронный метод может возвращать только void или Task.
static void Main()
{
// Create task and start it.
// ... Wait for it to complete.
Task task = new Task(AsyncMethod);
task.Start();
task.Wait();
}
public static async void AsyncMethod(){
await AnotherMehod();}
static async Task AnotherMehod() { //TODO}
Шаг 1: Подумайте об изменении на Task.Run(), если вы не хотите, чтобы планировщик участвовал. Я также предполагаю, что вы хотите дождаться окончания всех асинхронных задач.
public static AddExceptionEntry(Exception ex, string header = "")
{
Task t = Task.Factory.StartNew(() => AddExceptionEntry(ex, header));
tasks.Add(t);
WaitForExecutionAsync().ConfigureAwait(true);
}
public static async Task WaitForExecutionAsync()
{
if(tasks.Count >0)
await Task.WhenAll(tasks.ToArray());
// Raise Event.
}
Чтобы заблокировать, просто вызовите это для запуска sync vs async:
WaitForExecution().GetAwaiter().GetResult();