简体   繁体   English

C#.Net-如何使应用程序等待,直到在Library中创建的所有线程完成

[英]C# .Net - How to make application wait until all threads created in Library are finished

I am trying to create a logging library and things are good until the application shutdown is called. 我正在尝试创建一个日志记录库,在调用应用程序关闭之前,一切都很好。 When application shutdown is called, any unfinished thread is killed and the specific log is lost. 调用应用程序关闭时,所有未完成的线程都会被杀死,并且特定的日志也会丢失。

As of now application exits even before the first 10 threads are complete. 截至目前,应用程序甚至在前10个线程完成之前就退出了。 I want help on how to make the application wait until all threads created by library are done. 我需要有关如何使应用程序等待直到由库创建的所有线程完成的帮助。

NOTE: Requirement I got are like this. 注意:我的要求是这样的。 Modifications should be only in the class 'Logging' since this will be a library and will be provided to end users. 只能在“日志记录”类中进行修改,因为这将是一个库,并将提供给最终用户。 Handling of logging issues during app shutdown must be done within it. 必须在其中关闭应用程序关闭期间处理日志记录问题。 This is where I have trouble now. 这就是我现在遇到麻烦的地方。

Alternatively a solution like create an event in logging class to trigger all logging complete, and ask user to call app exit on that event is possible, but that I am trying to avoid since it adds that burden to end user and adds complexity for implementations. 另外,也可以使用诸如在日志记录类中创建事件以触发所有日志记录并要求用户在该事件上调用应用程序退出之类的解决方案,但是我试图避免这样做,因为这给最终用户增加了负担,并增加了实现的复杂性。 There is a possibility they may skip it, which I do not want. 他们可能会跳过它,我不希望这样做。 I am looking for a solution like user should do 'Logging.AddException(....)' and then forget about it. 我正在寻找一个解决方案,如用户应执行'Logging.AddException(....)',然后忘记它。

Please help. 请帮忙。 Provide comments if you are not clear about the idea. 如果您不清楚该想法,请提供评论。

Here is the full code abstract which you can put into a console application. 这是完整的代码摘要,您可以将其放入控制台应用程序。 Note: Look for comments in CASE 1 and CASE 2. 注意:在情况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.
    }
}

} }

As of now I myself found a workaround. 到目前为止,我自己已经找到了解决方法。

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

You can try 你可以试试

Task.WaitAll(tasks);

This waits for all of the provided Task objects to complete execution. 这等待所有提供的Task对象完成执行。

UPDATE : using async/await 更新 :使用异步/等待

With async and await, we formalize and clarify how asynchronous, non-blocking methods begin and end. 通过异步和等待,我们形式化并阐明了异步,非阻塞方法的开始和结束方式。 An async method can return only void or a Task. 异步方法只能返回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}

Step 1: Consider changing to Task.Run() if you do not want the scheduler to be involved. 步骤1:如果不想让调度程序参与,请考虑更改为Task.Run()。 I'm also assuming you want to wait until all async tasks finish. 我还假设您要等待所有异步任务完成。

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.
}

To Block just call this to run sync vs async: 要阻止,只需调用此命令即可运行同步与异步:

WaitForExecution().GetAwaiter().GetResult();

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM