发送 Exchange WS 电子邮件 - ThreadPool 中没有足够的空闲线程来完成操作
Sending Exchange WS emails - Not enough free threads in the ThreadPool to complete the operation
我需要克服这个错误:
There were not enough free threads in the ThreadPool to complete the operation.
我在排队一堆通信对象(使用 SSRS 报告)并通过 Exchange WS 发送它们时,80% 的时间都得到了它。使用 ThreadPool 生成报告并通过电子邮件发送。
这就是代码的样子,它基于 .Net 2.0 因此没有使用 TPL 或 Async/Await。
public static void QueueCorrespondence(ref Correspondence corr)
{
Queue corrQ = CorrespondenceQueue(corr.Medium);
// Create an AsyncOperation, using the CorrespondenceID as a unique id
AsyncOperation asOp = AsyncOperationManager.CreateOperation(corr.CorrespondenceID);
SendCorrespondencesInCurrentQueueArgs sendCorrespondencesInCurrentQueueArgs = new SendCorrespondencesInCurrentQueueArgs();
sendCorrespondencesInCurrentQueueArgs.AsOpArg = asOp;
sendCorrespondencesInCurrentQueueArgs.CorrQueueArg = corrQ;
lock (_correspondenceQs) {
if (corrQ.Count == 0) {
corrQ.Enqueue(corr);
ThreadPool.QueueUserWorkItem(new WaitCallback(SendCorrespondencesInCurrentQueue), sendCorrespondencesInCurrentQueueArgs);
} else {
corrQ.Enqueue(corr);
}
}
}
/// <summary>Do the processing necessary on the new thread</summary>
/// <param name="scicqa"></param>
/// <remarks>This runs on a background thread in the threadpool</remarks>
private static void SendCorrespondencesInCurrentQueue(SendCorrespondencesInCurrentQueueArgs scicqa)
{
Correspondence corr = null;
bool allReportsSuccessfullySent = false;
//Dequeues the correspondence from the queue and returns it (ByRef)
while (DequeueCorrespondence(ref corr, ref scicqa.CorrQueueArg)) {
try {
//This calls Exchange Web Services to send emails
Send(corr);
allReportsSuccessfullySent = true;
} catch (Exception ex) {
Incident.NewIncident("Unexpected Error", Incident.IncidentLevelEnum.ErrorLevel, ex, true, string.Format("Sending correspondence {0} via Medium {1}", corr.CorrespondenceID.ToString, corr.Medium));
} finally {
MailRoomArgs mra = new MailRoomArgs();
mra.CorrArg = corr;
mra.TransferSuccessArg = allReportsSuccessfullySent;
//Success or not, call the RaiseCorrespondenceSentEvent subroutine via this delegate.
scicqa.AsOpArg.Post(onCorrespondenceSentDelegate, mra);
}
}
}
/// <summary>Pull the next correspondence item off the queue, returning true if success</summary>
private static bool DequeueCorrespondence(ref Correspondence corr, ref Queue corrQ)
{
lock (_correspondenceQs) {
if (corrQ.Count > 0) {
corr = corrQ.Dequeue;
return true;
} else {
corr = null;
return false;
}
}
}
这是堆栈跟踪。看到上面的函数调用 Exchange WS 并且 BeginGetRequestStream 由于 ThreadPool 中缺少线程而抛出异常。
at System.Net.HttpWebRequest.BeginGetRequestStream(AsyncCallback callback, Object state)
at Microsoft.Exchange.WebServices.Data.EwsHttpWebRequest.Microsoft.Exchange.WebServices.Data.IEwsHttpWebRequest.BeginGetRequestStream(AsyncCallback callback, Object state)
at Microsoft.Exchange.WebServices.Data.ServiceRequestBase.GetWebRequestStream(IEwsHttpWebRequest request)
at Microsoft.Exchange.WebServices.Data.ServiceRequestBase.EmitRequest(IEwsHttpWebRequest request)
at Microsoft.Exchange.WebServices.Data.ServiceRequestBase.BuildEwsHttpWebRequest()
at Microsoft.Exchange.WebServices.Data.ServiceRequestBase.ValidateAndEmitRequest(IEwsHttpWebRequest& request)
at Microsoft.Exchange.WebServices.Data.SimpleServiceRequestBase.InternalExecute()
at Microsoft.Exchange.WebServices.Data.MultiResponseServiceRequest`1.Execute()
at Microsoft.Exchange.WebServices.Data.ExchangeService.InternalCreateItems(IEnumerable`1 items, FolderId parentFolderId, Nullable`1 messageDisposition, Nullable`1 sendInvitationsMode, ServiceErrorHandling errorHandling)
at Microsoft.Exchange.WebServices.Data.ExchangeService.CreateItem(Item item, FolderId parentFolderId, Nullable`1 messageDisposition, Nullable`1 sendInvitationsMode)
at Microsoft.Exchange.WebServices.Data.Item.InternalCreate(FolderId parentFolderId, Nullable`1 messageDisposition, Nullable`1 sendInvitationsMode)
at Microsoft.Exchange.WebServices.Data.Item.Save(WellKnownFolderName parentFolderName)
at XYZ.WM.CIMS.BusinessObjects.ExchangeServer.SendCorrespondence(Correspondence& corr)
at XYZ.WM.CIMS.BusinessObjects.Email.SendCorrespondence(Correspondence& corr)
at XYZ.WM.CIMS.BusinessObjects.CorrespondenceMediumBase.Send(Correspondence& corr)
at XYZ.WM.CIMS.BusinessObjects.CorrespondenceMediumBase.SendCorrespondencesInCurrentQueue(SendCorrespondencesInCurrentQueueArgs scicqa)
在主线程上发送电子邮件工作正常。几天后,我很确定如果我不在工作线程上调用 Exchange WS,就可以避免这个问题,如下所示。
无论我保存、发送还是发送并保存电子邮件,我仍然收到错误消息:
private exchangeService = new ExchangeService(3); //Exchange2010 SP2
public void SendCorrespondence(ref Correspondence corr)
{
exchangeService.Url = new Uri("https://webmail.XYZ.com.au/EWS/Exchange.asmx");
exchangeService.UseDefaultCredentials = true;
string[] emailAddresses = corr.SubscriptionObject.SubscriptionRecipients.EmailAddresses;
EmailMessage message = default(EmailMessage);
message = new EmailMessage(exchangeService);
if (emailAddresses.Count > 1) {
message.BccRecipients.Add(string.Join(RECIPIENTS_SEPARATOR, emailAddresses));
}
else if (emailAddresses.Count == 1) {
message.ToRecipients.Add(emailAddresses(0));
}
message.Subject = corr.SubscriptionObject.EmailSubject;
EmailAddress fromSender = new EmailAddress();
fromSender.Address = _instOpsSharedOutlookMailAccount;
fromSender.Name = _instOpsSharedMailAccountName;
fromSender.MailboxType = MailboxType.Mailbox;
message.From = fromSender;
foreach (ReportBase generatedReport in corr.GeneratedReports) {
message.Attachments.AddFileAttachment(generatedReport.GeneratedDocument.FullName);
}
message.Body = new BodyType();
message.Body.BodyType = BodyType.HTML;
message.Body.Text = corr.SubscriptionObject.EmailTemplate;
if (corr.SubscriptionObject.SendToDraft) {
//Saving causes the problem !!!!!!!!!!!
message.Save(WellKnownFolderName.Drafts);
} else if (Convert.ToBoolean(Code.GetCodeTextValue(PARENT_CODE, "ExchangeSaveMsgOnSend", RETURN_DEFAULT_STRING_IF_NO_DATA, "True"))) {
//Sending and Saving causes the problem !!!!!!!!!!!
message.SendAndSaveCopy();
} else {
//Sending causes the problem !!!!!!!!!!!
message.Send();
}
}
事实上,使用任何 Exchange 方法,例如:ExchangeService.ResolveName(logonID)
从池中的线程调用时都会产生错误。
有谁知道如何在不耗尽线程池的情况下发送电子邮件?我不想过多地重新设计这段代码,但很乐意实现一种简单的方法来避免在线程上发送电子邮件,如果有人知道怎么做的话。
Lotus Notes 不会出现此问题。当外部程序使用它发送电子邮件时,我使用 Exchange WS 来绕过 Outlook nag。我宁愿不使用 Outlook 客户端,但如果没有好的解决方案,我想我将不得不使用。使用 Smtp 客户端是不可能的(因为在组文件夹中保存草稿是一项主要功能)。我也尝试减少 ThreadPool 中的最大线程数,但我怀疑这没有什么不同。
您应该检查 ThreadPool.GetAvailableThreads() 并查看是否还有可用线程。那么你有两个选择。
1) 扩大线程的最大数量(但你应该避免它)
2) 等待一些线程完成
int availableThreads = 0;
int completionPortThreads = 0;
System.Threading.ThreadPool.GetAvailableThreads(out availableThreads, out completionPortThreads);
while (!(availableThreads > 1))
{
System.Threading.Thread.Sleep(100);
System.Threading.ThreadPool.GetAvailableThreads(out availableThreads, out completionPortThreads);
}
祝你好运!
我需要克服这个错误:
There were not enough free threads in the ThreadPool to complete the operation.
我在排队一堆通信对象(使用 SSRS 报告)并通过 Exchange WS 发送它们时,80% 的时间都得到了它。使用 ThreadPool 生成报告并通过电子邮件发送。
这就是代码的样子,它基于 .Net 2.0 因此没有使用 TPL 或 Async/Await。
public static void QueueCorrespondence(ref Correspondence corr)
{
Queue corrQ = CorrespondenceQueue(corr.Medium);
// Create an AsyncOperation, using the CorrespondenceID as a unique id
AsyncOperation asOp = AsyncOperationManager.CreateOperation(corr.CorrespondenceID);
SendCorrespondencesInCurrentQueueArgs sendCorrespondencesInCurrentQueueArgs = new SendCorrespondencesInCurrentQueueArgs();
sendCorrespondencesInCurrentQueueArgs.AsOpArg = asOp;
sendCorrespondencesInCurrentQueueArgs.CorrQueueArg = corrQ;
lock (_correspondenceQs) {
if (corrQ.Count == 0) {
corrQ.Enqueue(corr);
ThreadPool.QueueUserWorkItem(new WaitCallback(SendCorrespondencesInCurrentQueue), sendCorrespondencesInCurrentQueueArgs);
} else {
corrQ.Enqueue(corr);
}
}
}
/// <summary>Do the processing necessary on the new thread</summary>
/// <param name="scicqa"></param>
/// <remarks>This runs on a background thread in the threadpool</remarks>
private static void SendCorrespondencesInCurrentQueue(SendCorrespondencesInCurrentQueueArgs scicqa)
{
Correspondence corr = null;
bool allReportsSuccessfullySent = false;
//Dequeues the correspondence from the queue and returns it (ByRef)
while (DequeueCorrespondence(ref corr, ref scicqa.CorrQueueArg)) {
try {
//This calls Exchange Web Services to send emails
Send(corr);
allReportsSuccessfullySent = true;
} catch (Exception ex) {
Incident.NewIncident("Unexpected Error", Incident.IncidentLevelEnum.ErrorLevel, ex, true, string.Format("Sending correspondence {0} via Medium {1}", corr.CorrespondenceID.ToString, corr.Medium));
} finally {
MailRoomArgs mra = new MailRoomArgs();
mra.CorrArg = corr;
mra.TransferSuccessArg = allReportsSuccessfullySent;
//Success or not, call the RaiseCorrespondenceSentEvent subroutine via this delegate.
scicqa.AsOpArg.Post(onCorrespondenceSentDelegate, mra);
}
}
}
/// <summary>Pull the next correspondence item off the queue, returning true if success</summary>
private static bool DequeueCorrespondence(ref Correspondence corr, ref Queue corrQ)
{
lock (_correspondenceQs) {
if (corrQ.Count > 0) {
corr = corrQ.Dequeue;
return true;
} else {
corr = null;
return false;
}
}
}
这是堆栈跟踪。看到上面的函数调用 Exchange WS 并且 BeginGetRequestStream 由于 ThreadPool 中缺少线程而抛出异常。
at System.Net.HttpWebRequest.BeginGetRequestStream(AsyncCallback callback, Object state)
at Microsoft.Exchange.WebServices.Data.EwsHttpWebRequest.Microsoft.Exchange.WebServices.Data.IEwsHttpWebRequest.BeginGetRequestStream(AsyncCallback callback, Object state)
at Microsoft.Exchange.WebServices.Data.ServiceRequestBase.GetWebRequestStream(IEwsHttpWebRequest request)
at Microsoft.Exchange.WebServices.Data.ServiceRequestBase.EmitRequest(IEwsHttpWebRequest request)
at Microsoft.Exchange.WebServices.Data.ServiceRequestBase.BuildEwsHttpWebRequest()
at Microsoft.Exchange.WebServices.Data.ServiceRequestBase.ValidateAndEmitRequest(IEwsHttpWebRequest& request)
at Microsoft.Exchange.WebServices.Data.SimpleServiceRequestBase.InternalExecute()
at Microsoft.Exchange.WebServices.Data.MultiResponseServiceRequest`1.Execute()
at Microsoft.Exchange.WebServices.Data.ExchangeService.InternalCreateItems(IEnumerable`1 items, FolderId parentFolderId, Nullable`1 messageDisposition, Nullable`1 sendInvitationsMode, ServiceErrorHandling errorHandling)
at Microsoft.Exchange.WebServices.Data.ExchangeService.CreateItem(Item item, FolderId parentFolderId, Nullable`1 messageDisposition, Nullable`1 sendInvitationsMode)
at Microsoft.Exchange.WebServices.Data.Item.InternalCreate(FolderId parentFolderId, Nullable`1 messageDisposition, Nullable`1 sendInvitationsMode)
at Microsoft.Exchange.WebServices.Data.Item.Save(WellKnownFolderName parentFolderName)
at XYZ.WM.CIMS.BusinessObjects.ExchangeServer.SendCorrespondence(Correspondence& corr)
at XYZ.WM.CIMS.BusinessObjects.Email.SendCorrespondence(Correspondence& corr)
at XYZ.WM.CIMS.BusinessObjects.CorrespondenceMediumBase.Send(Correspondence& corr)
at XYZ.WM.CIMS.BusinessObjects.CorrespondenceMediumBase.SendCorrespondencesInCurrentQueue(SendCorrespondencesInCurrentQueueArgs scicqa)
在主线程上发送电子邮件工作正常。几天后,我很确定如果我不在工作线程上调用 Exchange WS,就可以避免这个问题,如下所示。
无论我保存、发送还是发送并保存电子邮件,我仍然收到错误消息:
private exchangeService = new ExchangeService(3); //Exchange2010 SP2
public void SendCorrespondence(ref Correspondence corr)
{
exchangeService.Url = new Uri("https://webmail.XYZ.com.au/EWS/Exchange.asmx");
exchangeService.UseDefaultCredentials = true;
string[] emailAddresses = corr.SubscriptionObject.SubscriptionRecipients.EmailAddresses;
EmailMessage message = default(EmailMessage);
message = new EmailMessage(exchangeService);
if (emailAddresses.Count > 1) {
message.BccRecipients.Add(string.Join(RECIPIENTS_SEPARATOR, emailAddresses));
}
else if (emailAddresses.Count == 1) {
message.ToRecipients.Add(emailAddresses(0));
}
message.Subject = corr.SubscriptionObject.EmailSubject;
EmailAddress fromSender = new EmailAddress();
fromSender.Address = _instOpsSharedOutlookMailAccount;
fromSender.Name = _instOpsSharedMailAccountName;
fromSender.MailboxType = MailboxType.Mailbox;
message.From = fromSender;
foreach (ReportBase generatedReport in corr.GeneratedReports) {
message.Attachments.AddFileAttachment(generatedReport.GeneratedDocument.FullName);
}
message.Body = new BodyType();
message.Body.BodyType = BodyType.HTML;
message.Body.Text = corr.SubscriptionObject.EmailTemplate;
if (corr.SubscriptionObject.SendToDraft) {
//Saving causes the problem !!!!!!!!!!!
message.Save(WellKnownFolderName.Drafts);
} else if (Convert.ToBoolean(Code.GetCodeTextValue(PARENT_CODE, "ExchangeSaveMsgOnSend", RETURN_DEFAULT_STRING_IF_NO_DATA, "True"))) {
//Sending and Saving causes the problem !!!!!!!!!!!
message.SendAndSaveCopy();
} else {
//Sending causes the problem !!!!!!!!!!!
message.Send();
}
}
事实上,使用任何 Exchange 方法,例如:ExchangeService.ResolveName(logonID)
从池中的线程调用时都会产生错误。
有谁知道如何在不耗尽线程池的情况下发送电子邮件?我不想过多地重新设计这段代码,但很乐意实现一种简单的方法来避免在线程上发送电子邮件,如果有人知道怎么做的话。
Lotus Notes 不会出现此问题。当外部程序使用它发送电子邮件时,我使用 Exchange WS 来绕过 Outlook nag。我宁愿不使用 Outlook 客户端,但如果没有好的解决方案,我想我将不得不使用。使用 Smtp 客户端是不可能的(因为在组文件夹中保存草稿是一项主要功能)。我也尝试减少 ThreadPool 中的最大线程数,但我怀疑这没有什么不同。
您应该检查 ThreadPool.GetAvailableThreads() 并查看是否还有可用线程。那么你有两个选择。
1) 扩大线程的最大数量(但你应该避免它)
2) 等待一些线程完成
int availableThreads = 0;
int completionPortThreads = 0;
System.Threading.ThreadPool.GetAvailableThreads(out availableThreads, out completionPortThreads);
while (!(availableThreads > 1))
{
System.Threading.Thread.Sleep(100);
System.Threading.ThreadPool.GetAvailableThreads(out availableThreads, out completionPortThreads);
}
祝你好运!