如何使用任务、后台线程或其他方法解决 Windows 服务中的挂起问题
How to use tasks, background threads or another method to solve a hanging issue in Windows Service
我想知道解决这个问题的最佳方法。
我创建了一个 Windows 连接到邮箱的服务,处理电子邮件,然后自行清理,等待一定时间并重复。
protected override void OnStart(string[] args)
{
this._mainTask = new Task(this.Poll, this._cancellationToken.Token, TaskCreationOptions.LongRunning);
this._mainTask.Start();
}
private void Poll()
{
CancellationToken cancellation = this._cancellationToken.Token;
TimeSpan interval = TimeSpan.Zero;
while (!cancellation.WaitHandle.WaitOne(interval))
{
using (IImapClient emailClient = new S22ImapClient())
{
ImapClientSettings chatSettings = ...;
emailClient.Connect(chatSettings); // CAN SOMETIMES HANG HERE
// SOME WORK DONE HERE
}
interval = this._waitAfterSuccessInterval;
// check the cancellation state.
if (cancellation.IsCancellationRequested)
{
break;
}
}
}
现在我正在使用第 3 方 IMAP 客户端 "S22.Imap"。当我有时创建电子邮件客户端对象时,它会在尝试登录时挂起。这反过来会无限期地挂起我的 Windows 服务。
public class S22ImapClient : IImapClient
{
private ImapClient _client;
public void Connect(ImapClientSettings imapClientSettings)
{
this._client = new ImapClient(
imapClientSettings.Host,
imapClientSettings.Port,
imapClientSettings.EmailAddress,
imapClientSettings.Password,
AuthMethod.Login,
true);
}
}
我如何更改 "S22ImapClient.Connect()" 调用以在幕后使用某种方法尝试连接一段时间,如果无法连接则中止?
这个解决方案也将用于我需要用邮件客户端做的任何事情,例如"GetMessage()"、"DeleteMessage()"等
您可以使用取消令牌源,并在它挂起太久的情况下给它一个时间来取消。否则,您只需要扩展第三方 class 并实现 Connect 方法的异步版本。这是未经测试的,但应该给你基本的想法。
private void Poll()
{
CancellationTokenSource source = new CancellationTokenSource();
TimeSpan interval = TimeSpan.Zero;
while (!source.Token.WaitHandle.WaitOne(interval))
{
using (IImapClient emailClient = new S22ImapClient())
{
ImapClientSettings chatSettings = ...;
var task = Task.Run(() =>
{
source.CancelAfter(TimeSpan.FromSeconds(5));
emailClient.Connect(chatSettings); // CAN SOMETIMES HANG HERE
}, source.Token);
// SOME WORK DONE HERE
}
interval = this._waitAfterSuccessInterval;
// check the cancellation state.
if (source.IsCancellationRequested)
{
break;
}
}
}
我决定停止使用 S22.Imap 电子邮件客户端来解决这个特定问题,并使用另一个第 3 方组件 ActiveUp MailSystem,因为它包括开箱即用的异步调用。
这样我就可以做这样的代码:
IAsyncResult connectResult = this._client.BeginConnectSsl(imapClientSettings.Host, imapClientSettings.Port, null);
if (!connectResult.AsyncWaitHandle.WaitOne(this._connectionTimeout))
{
throw new EmailTimeoutException(this._connectionTimeout);
}
我想知道解决这个问题的最佳方法。
我创建了一个 Windows 连接到邮箱的服务,处理电子邮件,然后自行清理,等待一定时间并重复。
protected override void OnStart(string[] args)
{
this._mainTask = new Task(this.Poll, this._cancellationToken.Token, TaskCreationOptions.LongRunning);
this._mainTask.Start();
}
private void Poll()
{
CancellationToken cancellation = this._cancellationToken.Token;
TimeSpan interval = TimeSpan.Zero;
while (!cancellation.WaitHandle.WaitOne(interval))
{
using (IImapClient emailClient = new S22ImapClient())
{
ImapClientSettings chatSettings = ...;
emailClient.Connect(chatSettings); // CAN SOMETIMES HANG HERE
// SOME WORK DONE HERE
}
interval = this._waitAfterSuccessInterval;
// check the cancellation state.
if (cancellation.IsCancellationRequested)
{
break;
}
}
}
现在我正在使用第 3 方 IMAP 客户端 "S22.Imap"。当我有时创建电子邮件客户端对象时,它会在尝试登录时挂起。这反过来会无限期地挂起我的 Windows 服务。
public class S22ImapClient : IImapClient
{
private ImapClient _client;
public void Connect(ImapClientSettings imapClientSettings)
{
this._client = new ImapClient(
imapClientSettings.Host,
imapClientSettings.Port,
imapClientSettings.EmailAddress,
imapClientSettings.Password,
AuthMethod.Login,
true);
}
}
我如何更改 "S22ImapClient.Connect()" 调用以在幕后使用某种方法尝试连接一段时间,如果无法连接则中止?
这个解决方案也将用于我需要用邮件客户端做的任何事情,例如"GetMessage()"、"DeleteMessage()"等
您可以使用取消令牌源,并在它挂起太久的情况下给它一个时间来取消。否则,您只需要扩展第三方 class 并实现 Connect 方法的异步版本。这是未经测试的,但应该给你基本的想法。
private void Poll()
{
CancellationTokenSource source = new CancellationTokenSource();
TimeSpan interval = TimeSpan.Zero;
while (!source.Token.WaitHandle.WaitOne(interval))
{
using (IImapClient emailClient = new S22ImapClient())
{
ImapClientSettings chatSettings = ...;
var task = Task.Run(() =>
{
source.CancelAfter(TimeSpan.FromSeconds(5));
emailClient.Connect(chatSettings); // CAN SOMETIMES HANG HERE
}, source.Token);
// SOME WORK DONE HERE
}
interval = this._waitAfterSuccessInterval;
// check the cancellation state.
if (source.IsCancellationRequested)
{
break;
}
}
}
我决定停止使用 S22.Imap 电子邮件客户端来解决这个特定问题,并使用另一个第 3 方组件 ActiveUp MailSystem,因为它包括开箱即用的异步调用。
这样我就可以做这样的代码:
IAsyncResult connectResult = this._client.BeginConnectSsl(imapClientSettings.Host, imapClientSettings.Port, null);
if (!connectResult.AsyncWaitHandle.WaitOne(this._connectionTimeout))
{
throw new EmailTimeoutException(this._connectionTimeout);
}