ユーザーがウィンドウ内の特定のボタンをクリック/押し、アプリケーションがいくつかのチェックを行い、2、3のメールを送信するかどうかを決定し、メッセージを含む別のウィンドウを表示するアプリケーションを開発しています。
私の問題は、2つの電子メールを送信するとプロセスが著しく遅くなり、送信中に最初のウィンドウが数秒間(〜8)フリーズしたように見えることです。
これらのメールをバックグラウンドで送信して、すぐに次のウィンドウを表示する方法はありますか?
「Xクラスを使用する」または「Xメソッドを使用する」だけで答えを制限しないでください。私はまだ言語に精通していないため、さらに情報をいただければ幸いです。
ありがとう。
.NET 4.5の時点で、SmtpClientは非同期待機可能メソッド SendMailAsync
を実装しています。その結果、電子メールを非同期で送信するには、次のようにします。
public async Task SendEmail(string toEmailAddress, string emailSubject, string emailMessage)
{
var message = new MailMessage();
message.To.Add(toEmailAddress);
message.Subject = emailSubject;
message.Body = emailMessage;
using (var smtpClient = new SmtpClient())
{
await smtpClient.SendMailAsync(message);
}
}
これは小さな作業単位であるため、スレッド化には ThreadPool.QueueUserWorkItem を使用する必要があります。 SmtpClient クラスを使用してメールを送信する場合、 SendCompleted イベントを処理してユーザーにフィードバックを送信できます。
ThreadPool.QueueUserWorkItem(t =>
{
SmtpClient client = new SmtpClient("MyMailServer");
MailAddress from = new MailAddress("[email protected]", "My Name", System.Text.Encoding.UTF8);
MailAddress to = new MailAddress("[email protected]");
MailMessage message = new MailMessage(from, to);
message.Body = "The message I want to send.";
message.BodyEncoding = System.Text.Encoding.UTF8;
message.Subject = "The subject of the email";
message.SubjectEncoding = System.Text.Encoding.UTF8;
// Set the method that is called back when the send operation ends.
client.SendCompleted += new SendCompletedEventHandler(SendCompletedCallback);
// The userState can be any object that allows your callback
// method to identify this send operation.
// For this example, I am passing the message itself
client.SendAsync(message, message);
});
private static void SendCompletedCallback(object sender, AsyncCompletedEventArgs e)
{
// Get the message we sent
MailMessage msg = (MailMessage)e.UserState;
if (e.Cancelled)
{
// Prompt user with "send cancelled" message
}
if (e.Error != null)
{
// Prompt user with error message
}
else
{
// Prompt user with message sent!
// as we have the message object we can also display who the message
// was sent to etc
}
// finally dispose of the message
if (msg != null)
msg.Dispose();
}
これにより、毎回新しいSMTPクライアントを作成することで、同時にメールを送信できます。
単純に別のスレッドでメッセージを送信するのはそれほど複雑ではありません。
using System.Net.Mail;
Smtp.SendAsync(message);
または、単に非同期で送信するのではなく、個別のスレッドでメッセージ全体を作成する場合:
using System.Threading;
using System.Net.Mail;
var sendMailThread = new Thread(() => {
var message=new MailMessage();
message.From="from e-mail";
message.To="to e-mail";
message.Subject="Message Subject";
message.Body="Message Body";
SmtpMail.SmtpServer="SMTP Server Address";
SmtpMail.Send(message);
});
sendMailThread.Start();
サンプル
using System;
using System.Net;
using System.Net.Mail;
using System.Net.Mime;
using System.Threading;
using System.ComponentModel;
namespace Examples.SmptExamples.Async
{
public class SimpleAsynchronousExample
{
static bool mailSent = false;
private static void SendCompletedCallback(object sender, AsyncCompletedEventArgs e)
{
// Get the unique identifier for this asynchronous operation.
String token = (string) e.UserState;
if (e.Cancelled)
{
Console.WriteLine("[{0}] Send canceled.", token);
}
if (e.Error != null)
{
Console.WriteLine("[{0}] {1}", token, e.Error.ToString());
} else
{
Console.WriteLine("Message sent.");
}
mailSent = true;
}
public static void Main(string[] args)
{
// Command line argument must the the SMTP Host.
SmtpClient client = new SmtpClient(args[0]);
// Specify the e-mail sender.
// Create a mailing address that includes a UTF8 character
// in the display name.
MailAddress from = new MailAddress("[email protected]",
"Jane " + (char)0xD8+ " Clayton",
System.Text.Encoding.UTF8);
// Set destinations for the e-mail message.
MailAddress to = new MailAddress("[email protected]");
// Specify the message content.
MailMessage message = new MailMessage(from, to);
message.Body = "This is a test e-mail message sent by an application. ";
// Include some non-ASCII characters in body and subject.
string someArrows = new string(new char[] {'\u2190', '\u2191', '\u2192', '\u2193'});
message.Body += Environment.NewLine + someArrows;
message.BodyEncoding = System.Text.Encoding.UTF8;
message.Subject = "test message 1" + someArrows;
message.SubjectEncoding = System.Text.Encoding.UTF8;
// Set the method that is called back when the send operation ends.
client.SendCompleted += new
SendCompletedEventHandler(SendCompletedCallback);
// The userState can be any object that allows your callback
// method to identify this send operation.
// For this example, the userToken is a string constant.
string userState = "test message1";
client.SendAsync(message, userState);
Console.WriteLine("Sending message... press c to cancel mail. Press any other key to exit.");
string answer = Console.ReadLine();
// If the user canceled the send, and mail hasn't been sent yet,
// then cancel the pending operation.
if (answer.StartsWith("c") && mailSent == false)
{
client.SendAsyncCancel();
}
// Clean up.
message.Dispose();
Console.WriteLine("Goodbye.");
}
}
}
ここにasyncを伴うファイアアンドフォーゲットアプローチ .Net 4.5.2+を使用しています:
BackgroundTaskRunner.FireAndForgetTaskAsync(async () =>
{
SmtpClient smtpClient = new SmtpClient(); // using configuration file settings
MailMessage message = new MailMessage(); // TODO: Initialize appropriately
await smtpClient.SendMailAsync(message);
});
backgroundTaskRunnerは次のとおりです。
public static class BackgroundTaskRunner
{
public static void FireAndForgetTask(Action action)
{
HostingEnvironment.QueueBackgroundWorkItem(cancellationToken => // .Net 4.5.2+ required
{
try
{
action();
}
catch (Exception e)
{
// TODO: handle exception
}
});
}
/// <summary>
/// Using async
/// </summary>
public static void FireAndForgetTaskAsync(Func<Task> action)
{
HostingEnvironment.QueueBackgroundWorkItem(async cancellationToken => // .Net 4.5.2+ required
{
try
{
await action();
}
catch (Exception e)
{
// TODO: handle exception
}
});
}
}
Azure App Servicesの魅力のように機能します。
これが少し曖昧だからといって...簡単に説明します...
C#/。netなどで非同期または並列作業を行う方法はたくさんあります。
必要な処理を行う最も速い方法は、UIのロックを回避するバックグラウンドワーカースレッドを使用することです。
バックグラウンドワーカースレッドに関するヒント:UIを直接更新することはできません(スレッドアフィニティとマーシャリングは、対処方法を学ぶだけのものです...)
もう1つ考慮すべき点は、標準のSystem.Net.Mailタイプのものを使用してメールを送信する場合...ロジックの作成方法に注意してください。何らかの方法ですべてを分離して何度も呼び出すと、毎回メールサーバーへの接続を切断して再構築する必要があり、認証などに関連する遅延により、全体が不必要に遅くなります。可能な場合は、メールサーバーへの単一のオープン接続を介して複数の電子メールを送信します。
これを試して:
var client = new System.Net.Mail.SmtpClient("smtp.server");
var message = new System.Net.Mail.MailMessage() { /* provide its properties */ };
client.SendAsync(message, null);
SmtpClient クラスを使用し、System.Net.Mail名前空間で SendAsync メソッドを使用します。
やりたいことは、別のスレッドで電子メールタスクを実行して、他のスレッドが電子メールを処理している間にメインコードが処理を続行できるようにすることです。
これを行う方法のチュートリアルを次に示します。 Threading Tutorial C#
.NET 4.0で Task Parallel Library を使用すると、次のことができます。
Parllel.Invoke(() => { YourSendMailMethod(); });
また、Parallel.Invoke()と明示的なタスク管理に関する cristina manu's ブログ投稿も参照してください。