C# Net-如何使应用程序等待库中创建的所有线程完成
我正在尝试创建一个日志库,在调用应用程序关闭之前一切都很好。调用应用程序关闭时,将终止所有未完成的线程,并丢失特定日志 到目前为止,应用程序甚至在前10个线程完成之前就退出了。我需要关于如何使应用程序等待库创建的所有线程完成的帮助 注: 我得到的要求是这样的。修改只应在“Logging”类中进行,因为这将是一个库,并将提供给最终用户。应用程序关闭期间的日志记录问题处理必须在应用程序内完成。这就是我现在遇到麻烦的地方 或者,在logging类中创建一个事件来触发所有日志记录完成,并要求用户在该事件上调用appexit这样的解决方案是可能的,但我试图避免这种情况,因为这会给最终用户增加负担,并增加实现的复杂性。他们可能会跳过它,这是我不想要的。我正在寻找一个解决方案,像用户应该做“Logging.AddException(..”),然后忘记它 请帮忙。如果您对想法不清楚,请提供意见 下面是可以放入控制台应用程序的完整代码摘要。 注意:在案例1和案例2中查找注释。C# Net-如何使应用程序等待库中创建的所有线程完成,c#,multithreading,frameworks,shared-libraries,application-shutdown,C#,Multithreading,Frameworks,Shared Libraries,Application Shutdown,我正在尝试创建一个日志库,在调用应用程序关闭之前一切都很好。调用应用程序关闭时,将终止所有未完成的线程,并丢失特定日志 到目前为止,应用程序甚至在前10个线程完成之前就退出了。我需要关于如何使应用程序等待库创建的所有线程完成的帮助 注: 我得到的要求是这样的。修改只应在“Logging”类中进行,因为这将是一个库,并将提供给最终用户。应用程序关闭期间的日志记录问题处理必须在应用程序内完成。这就是我现在遇到麻烦的地方 或者,在logging类中创建一个事件来触发所有日志记录完成,并要求用户在该事件
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秒。非托管主机可以通过使用枚举调用该方法来更改此执行时间