c# - 发送 Exchange WS 电子邮件 - 线程池中没有足够的可用线程来完成操作

标签 c# .net multithreading threadpool exchangewebservices

我需要克服这个错误:

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,则可以避免该问题,如下所示。

enter image description here

无论我保存、发送还是 SendAndSave 电子邮件,我仍然会收到错误消息:

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 发送电子邮件时,我使用 Exchange WS 来解决 Outlook 的问题。我不想使用 Outlook 客户端,但如果没有好的解决方案,我想我将不得不使用。使用 Smtp 客户端不太可能(因为在组文件夹中保存草稿是一项主要功能)。我还尝试减少线程池中的最大线程数,但我怀疑这没有什么区别。

最佳答案

您应该检查 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); 
}

祝你好运!

关于c# - 发送 Exchange WS 电子邮件 - 线程池中没有足够的可用线程来完成操作,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31019235/

相关文章:

c# - 为什么实现 'IList<T>' 需要定义两个 'GetEnumerator' 方法?

java - 在异步任务中更新 UI 线程

java - 如何限制第三方Java方法的执行时间

c# - 在c#中锁定代码部分

c# - 如何从谓词对象中删除重复项?

c# - 可选参数无法正常工作

java多线程运行,当一个线程找到解决方案时停止线程

c# - 如何强制 EF 使用连接而不是拆分复杂的查询?

c# - 使用 XElement 时如何输入文本

C# 获取自定义控件内控件的事件