下面是我運行的簡單代碼IHostedService
internal class Program {
public static Task Main(string[] args) {
var host = new HostBuilder().ConfigureServices((hostcontext, services) => {
services.AddHostedService<MyService>();
}).Build();
host.Run();
return Task.CompletedTask;
}
}
public class MyService : IHostedService {
public Task StartAsync(CancellationToken cancellationToken) {
return Task.Run(() => {
while (true) {
Console.WriteLine("Starting Service");
}
});
}
public Task StopAsync(CancellationToken cancellationToken) {
Console.WriteLine("Stopping service");
return Task.CompletedTask;
}
}
因此,當我想通過在控制臺中按 ctrl C 來停止服務時,我希望看到服務停止并且控制臺列印“正在停止服務”。
但是當我按Ctrl C時,服務繼續在無限回圈中運行,我不明白。
我認為Task.Run()在執行緒池中排隊一個作業項,然后是執行緒池中的一個后臺執行緒來獲取該作業,所以在我的示例中,它是一個作業執行緒(作業執行緒的 id 是 4,主執行緒的 id 是 1)執行while 回圈。所以當我按 ctrl S 時,服務應該停止,那么為什么正在運行的后臺執行緒停止服務被停止,不是當應用程式終止時,所有后臺作業/執行緒也終止了嗎?我的意思是,如果Task.Run()運行創建了一個前臺執行緒,那么我可以理解,因為所有前臺執行緒都需要在應用程式停止之前完成。
PS:
我可以通過CancellationToken來停止while回圈,我知道我可以這樣做,并且在while回圈中,我檢查令牌是否被呼叫等等......
but I don't understand why I have to do that, because the running thread is a background thread, not a foreground thread, so why all background threads need to finish first the the StopAsync() can be invoked? i.e how does a running background thread stops the exection flow reach to StopAsync()?
uj5u.com熱心網友回復:
如果要停止任務,請嘗試在 MyServiceClass 中使用取消令牌
這是我的例子:
public class MyService : IHostedService
{
private CancellationTokenSource _ts;
public Task StartAsync(CancellationToken cancellationToken)
{
_ts = new CancellationTokenSource();
return Task.Factory.StartNew(() =>
{
while (true)
{
Console.WriteLine("Starting Service");
Thread.Sleep(500);
if (_ts.Token.IsCancellationRequested)
{
Console.WriteLine("Stopping service");
break;
}
}
}, _ts.Token);
}
public Task StopAsync(CancellationToken cancellationToken)
{
_ts.Cancel();
return Task.CompletedTask;
}
}
uj5u.com熱心網友回復:
Ctrl C 不會直接終止您的控制臺應用程式的原因,就像您可能習慣于從“普通”控制臺應用程式一樣,是因為 .NET 實際上支持防止 Ctrl C 終止應用程式,并且對于應用程式對 Ctrl C 和“做事”做出反應。
您正在使用的托管框架已使用此系統,一個事件,以防止您的應用程式被強制終止以及接管關閉程序。
事件本身是Console.CancelKeyPress,這是一個可取消的事件,這意味著您可以從事件處理程式回傳并在 EventArgs 物件上設定一個標志,以向呼叫您的事件處理程式的代碼發出信號,表明您希望選擇退出 Ctrl C 的默認處理.
托管框架已經做到了這一點。
您可以在此處查看確切的代碼:Microsoft.Extensions.Hosting/Internal/ConsoleLifetime.cs @48-52:
Console.CancelKeyPress = (sender, e) =>
{
e.Cancel = true;
ApplicationLifetime.StopApplication();
};
除了取消正常的關閉程序之外,宿主框架提供的事件處理程式還取消了一個 CancellationToken,它是傳遞給 StartAsync 的令牌。正如您所說,如果您將此令牌傳遞給您的任務并對其被取消做出反應,您的應用程式將關閉。
呼叫ApplicationLifetime.StopApplication最終以Microsoft.Extensions.Hosting/Internal/ApplicationLifetime.cs @102-112結束:
private void ExecuteHandlers(CancellationTokenSource cancel)
{
// Noop if this is already cancelled
if (cancel.IsCancellationRequested)
{
return;
}
// Run the cancellation token callbacks
cancel.Cancel(throwOnFirstException: false);
}
所以你有它。您的應用程式一直在無限回圈中旋轉的原因正是因為托管框架阻止了正常關閉,而是讓您的任務有機會以有序的方式完成。由于您的任務會忽略此請求,并繼續運行,因此當您按下 Ctrl C 時,您的行程永遠不會終止。
轉載請註明出處,本文鏈接:https://www.uj5u.com/caozuo/424202.html
