C# Net-如何使应用程序等待库中创建的所有线程完成

C# Net-如何使应用程序等待库中创建的所有线程完成,c#,multithreading,frameworks,shared-libraries,application-shutdown,C#,Multithreading,Frameworks,Shared Libraries,Application Shutdown,我正在尝试创建一个日志库,在调用应用程序关闭之前一切都很好。调用应用程序关闭时,将终止所有未完成的线程,并丢失特定日志 到目前为止,应用程序甚至在前10个线程完成之前就退出了。我需要关于如何使应用程序等待库创建的所有线程完成的帮助 注: 我得到的要求是这样的。修改只应在“Logging”类中进行,因为这将是一个库,并将提供给最终用户。应用程序关闭期间的日志记录问题处理必须在应用程序内完成。这就是我现在遇到麻烦的地方 或者,在logging类中创建一个事件来触发所有日志记录完成,并要求用户在该事件

我正在尝试创建一个日志库,在调用应用程序关闭之前一切都很好。调用应用程序关闭时,将终止所有未完成的线程,并丢失特定日志

到目前为止,应用程序甚至在前10个线程完成之前就退出了。我需要关于如何使应用程序等待库创建的所有线程完成的帮助

注: 我得到的要求是这样的。修改只应在“Logging”类中进行,因为这将是一个库,并将提供给最终用户。应用程序关闭期间的日志记录问题处理必须在应用程序内完成。这就是我现在遇到麻烦的地方

或者,在logging类中创建一个事件来触发所有日志记录完成,并要求用户在该事件上调用appexit这样的解决方案是可能的,但我试图避免这种情况,因为这会给最终用户增加负担,并增加实现的复杂性。他们可能会跳过它,这是我不想要的。我正在寻找一个解决方案,像用户应该做“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.
    }
}
使用系统;
使用System.Collections.Generic;
使用系统诊断;
使用System.Linq;
使用系统文本;
使用系统线程;
使用System.Threading.Tasks;
命名空间多线程保持活动
{
班级计划
{
静态void Main(字符串[]参数)
{
LogLoadTest();
AddExceptionEntryAsync(新异常(“上次异常”));
/*
*用例1:启用下面的行,您将看到需要多长时间。
*请注意,如果下面未注释,则不会触发currentDomain_ProcessExit
*/
//WriteLine(“主线程等待覆盖”);
//Console.ReadLine();
}
静态无效LogLoadTest()
{
//在现实世界中,这将从应用程序的任何位置(如启动)调用,或者在启动应用程序关闭之后调用。
//:注意:与这里的示例不同,它永远不会在循环中,我不希望在这个类中处理多线程。
//我计划分配给日志类的职责。
//而且,类日志记录将在一个单独的有符号程序集中,在这个程序集中,这个类(“程序”)的用户不应该担心多线程。
任务t;
对于(int i=0;i<40;i++)
{
t=Logging.AddExceptionEntryAsync(新异常(“Hello异常”+i)、“头信息”+i);
}
}
}
公共类日志记录
{
静态列表任务=新建列表();
静态AppDomain-currentDomain;
静态日志记录()
{
currentDomain=AppDomain.currentDomain;
currentDomain.ProcessExit+=currentDomain\u ProcessExit;
}
公共静态异步任务AddExceptionEntryAsync(异常ex,字符串头=“”)
{
Task t=Task.Factory.StartNew(()=>AddExceptionEntry(ex,header));
任务。添加(t);
等待t;
}
公共静态void AddExceptionEntry(异常示例,字符串头)
{
/*异常处理并写入文件或数据库。这可能会导致文件锁定或
*网络或任何其他需要1秒到5分钟延迟的情况*/
Sleep(newrandom().Next(11000));
控制台写入线(例如消息);
}
静态无效currentDomain_ProcessExit(对象发送方,事件参数e)
{
WriteLine(“刚才应用程序关闭触发”);
Process.GetCurrentProcess().WaitForExit();//第一次尝试。
//Task.WaitAll(tasks.ToArray());//第二次尝试
while(tasks.Any(t=>!t.IsCompleted))//第三次尝试。
{
}
/*用例2:如果工作正常,这将在控制台中显示为最后一个
*应用程序的消息,并将等待用户。此操作现在不起作用*/
Console.WriteLine(“全部完成”);//如果工作正常,则应显示此消息
Console.ReadLine();//出于测试目的,请在每个线程完成后等待用户的输入。检查控制台中的所有40个线程。
}
}
}你可以试试

Task.WaitAll(tasks);
这将等待所有提供的任务对象完成执行

更新:使用异步/等待

通过async和await,我们形式化并阐明了异步、非阻塞方法是如何开始和结束的。异步方法只能返回void或任务

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:如果不希望调度器参与,请考虑更改为Tea.我还假设您希望等待所有异步任务完成

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();

到现在为止,我自己找到了一个解决办法

    /// <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());
    }
//
///使当前线程等待,直到任何挂起的消息/异常/日志完全写入各自的源中。
///在应用程序关闭之前调用此方法,以确保所有日志都已正确保存。
/// 
公共静态void WaitForLogComplete()
{
Task.WaitAll(tasks.Values.ToArray());
}

使用线程函数join()进行了尝试?如果退出,可以为每个正在运行的线程调用该方法。因此,程序会一直等到它们完成。为什么这个函数:
AddExceptionEntryAsync(异常示例,字符串头)
没有标记为
async
,只在其中使用一个
任务。延迟
?你有没有读过有关的文件?特别是这一部分“所有ProcessExit事件处理程序的总执行时间都是有限的,就像进程关闭时所有终结器的总执行时间都是有限的一样。默认值为2秒。非托管主机可以通过使用枚举调用该方法来更改此执行时间