我有一个名为WCSession* session = [WCSession defaultSession];
[session updateApplicationContext:applicationDict error:nil];
的方法,它会发送一个提供它的电子邮件列表,并且我试图弄清楚如何使用多个线程来加速这个过程电子邮件:
asyncStartList
public async Task asyncStartList()
{
Stopwatch stopWatch = new Stopwatch();
stopWatch.Start();
for (int i = 0; i < listLength; i++)
{
currentMailAddress = emailingList[i];
await Task.Run(() => MailingFunction());
currentMailAddress = "";
Console.WriteLine("Your mail to {0} was successfully sent!", emailingList[i]);
}
stopWatch.Stop();
TimeSpan ts = stopWatch.Elapsed;
string elapsedTime = String.Format("{0:00}:{1:00}:{2:00}.{3:00}",
ts.Hours, ts.Minutes, ts.Seconds, ts.Milliseconds / 10);
Console.WriteLine("Time for completion " + elapsedTime);
Console.ReadLine();
}
是一个简单的SmtpClient和邮件消息。
答案 0 :(得分:2)
您的解决方案实际上并不是并行运行,因为您等待每个发送操作。您可以使用paralel foreach /作为关键字。否则,您必须等待所有发送操作执行完毕。
public async Task asyncStartList()
{
Stopwatch stopWatch = new Stopwatch();
stopWatch.Start();
// option 1
Task[] tasks = emailingList.Select(s => Task.Run(() => { SendEmail(s); }).ToArray();
Task.WaitAll(tasks);
// option 1 end
// option 2
Parallel.ForEach(emailingList, email =>
{
SendEmail(email);
});
// option 2 end
// option 3
Parallel.For(0, emailingList.Length, i =>
{
SendEmail(emailingList[i]);
});
// option 3 end
stopWatch.Stop();
TimeSpan ts = stopWatch.Elapsed;
string elapsedTime = String.Format("{0:00}:{1:00}:{2:00}.{3:00}", ts.Hours, ts.Minutes, ts.Seconds, ts.Milliseconds / 10);
Console.WriteLine("Time for completion " + elapsedTime);
Console.ReadLine();
}
private void SendEmail(string emailAddress)
{
// Do send operation
}
答案 1 :(得分:1)
使用Parallel.ForEach
命名空间中的System.Threading.Tasks
。相反,for int i = 0;...
使用Parallel.ForEach(emailingList, address => {...})
答案 2 :(得分:0)
如果您的解决方案的性能是CPU-bound,那就是您想要使用并行线程。如果您的解决方案受到其他内容的约束 - 例如电子邮件服务器处理请求的能力 - 您实际应该使用的是async
,这更简单,更安全。
在这种情况下,有很多种方法可以使用异步,但这里有一个简短易用的简单模式:
await Task.WhenAll
(
emailingList.Select( async address => MailingFunctionAsync(address) )
);
是的,这就是它的全部。这假设您的电子邮件客户端不仅具有MailingFunction()
方法,还具有MailingFunctionAsync()
方法(例如,使用Outlook&#39; s SendAsync()
方法或类似方法)。
以下是从this question中窃取的MailingFunctionAsync()
示例:
public async Task MailingFunctionAsync(string toEmailAddress)
{
var message = new MailMessage();
message.To.Add(toEmailAddress);
message.Subject = SOME_SUBJECT;
message.Body = SOME_BODY;
using (var smtpClient = new SmtpClient())
{
await smtpClient.SendMailAsync(message);
}
}
答案 3 :(得分:0)
我只是觉得id很重要。这里常见的答案是使用Parallel.ForEach
(除了你应该考虑的 John Wus 答案之外)。虽然一开始Parallel.ForEach
似乎是一个简单而好的主意,但它实际上并不是最理想的方法。
这是问题
Parallel.ForEach
使用线程池。此外, IO绑定操作将阻止那些等待设备响应并占用资源的线程。
在这种情况下,发送邮件显然是 I / O ,因此理想的消费代码将是异步的。
此外。
要正确使用 .NET 的异步和并行功能,您还应该了解 I / O线程的概念。
并非程序中的所有内容都会占用CPU时间。当线程尝试从磁盘上的文件读取数据或通过网络发送TCP / IP数据包时,它唯一要做的就是将实际工作委托给设备;磁盘或网络适配器;并等待结果。
花时间等待是非常昂贵的。即使通过线程休眠并且在等待结果时不消耗 CPU 时间,它也不会真正得到回报,因为它浪费了系统资源。
简单来说,每个线程都拥有堆栈变量,本地存储等内存。此外,您拥有的线程越多,在它们之间切换所需的时间就越多。
虽然Parallel.ForEach
的优点是易于实现,但您也可以设置Max Degree of Parallelism等选项。
那你能做什么......
您最好对并发任务使用async
/ await
模式和/或某种类型的限制,另一个简洁的解决方案是ActionBlock Class库中的TPL dataflow。
数据流示例
var block = new ActionBlock<MySomething>(
mySomething => MyMethodAsync(mySomething),
new ExecutionDataflowBlockOptions { MaxDegreeOfParallelism = 50 });
foreach (var something in ListOfSomethings)
{
block.Post(something );
}
block.Complete();
await block.Completion;
这种方法为您提供 Asynchrony ,它还为您提供MaxDegreeOfParallelism
,它不会浪费资源,并让 IO 成为 IO 而不会占用不必要的资源
免责声明 ,DataFlow可能不是您想要的地方,但我只是认为id会为您提供有关不同内容的更多信息 提供的方法