限制电子邮件发送过程的速度
抱歉,标题有点蹩脚,我无法准确表达。
编辑:我应该注意到,这是一个控制台 c# 应用程序,
我已经设计出了一个系统原型,其工作方式如下(这是粗略的伪代码):
var collection = grabfromdb();
foreach (item in collection) {
SendAnEmail();
}
SendAnEmail:
SmtpClient mailClient = new SmtpClient;
mailClient.SendCompleted += new SendCompletedEventHandler(SendComplete);
mailClient.SendAsync('the mail message');
SendComplete:
if (anyErrors) {
errorHandling()
}
else {
HitDBAndMarkAsSendOK();
}
显然这个设置并不理想。 如果初始集合有 10,000 条记录,那么它将以相当短的顺序新建 10,000 个 smtpclient 实例,只要它能够逐步遍历行 - 并且可能会在此过程中爆炸。
我理想的最终结果是同时发出 10 封并发电子邮件。
我想到了一个 hacky 解决方案:添加一个计数器,当调用 SendAnEmail() 时该计数器递增,并在发送 SendComplete 时递减。 在初始循环中调用 SendAnEmail() 之前,检查计数器,如果计数器太高,则休眠一小段时间,然后再次检查。
我不确定这是一个好主意,并且认为蜂巢思维会有办法正确地做到这一点。
我对线程知之甚少,不确定它是否适合在这里使用。 例如,在后台线程中发送电子邮件,首先检查子线程的数量,以确保没有使用太多子线程。 或者,如果内置某种类型的“线程节流”。
更新
按照 Steven A. Lowe 的建议,我现在拥有:
- 一个包含我的电子邮件和唯一密钥的字典(这是电子邮件 que
- A FillQue 方法,它填充字典
- ProcessQue 方法,它是一个后台线程,它会检查队列中的任何电子邮件,
- 会再次调用 FillQue 来删除队列中的电子邮件。
并 我想我错过了背景线程的船,我应该为字典中的每个项目生成其中一个吗?如果电子邮件没有找到,我怎样才能让线程“徘徊”?清空线程结束。
最后更新
我在后台线程中放置了一个“while(true) {}”。如果队列为空,它会等待几秒钟,然后再次尝试,如果队列重复为空,我会“中断”。 ' while,程序结束......工作正常,但我有点担心'while(true)'业务。
Sorry the title is a bit crappy, I couldn't quite word it properly.
Edit: I should note this is a console c# app
I've prototyped out a system that works like so (this is rough pseudo-codeish):
var collection = grabfromdb();
foreach (item in collection) {
SendAnEmail();
}
SendAnEmail:
SmtpClient mailClient = new SmtpClient;
mailClient.SendCompleted += new SendCompletedEventHandler(SendComplete);
mailClient.SendAsync('the mail message');
SendComplete:
if (anyErrors) {
errorHandling()
}
else {
HitDBAndMarkAsSendOK();
}
Obviously this setup is not ideal. If the initial collection has, say 10,000 records, then it's going to new up 10,000 instances of smtpclient in fairly short order as quickly as it can step through the rows - and likely asplode in the process.
My ideal end game is to have something like 10 concurrent email going out at once.
A hacky solution comes to mind: Add a counter, that increments when SendAnEmail() is called, and decrements when SendComplete is sent. Before SendAnEmail() is called in the initial loop, check the counter, if it's too high, then sleep for a small period of time and then check it again.
I'm not sure that's such a great idea, and figure the SO hive mind would have a way to do this properly.
I have very little knowledge of threading and not sure if it would be an appropriate use here. Eg sending email in a background thread, first check the number of child threads to ensure there's not too many being used. Or if there is some type of 'thread throttling' built in.
Update
Following in the advice of Steven A. Lowe, I now have:
- A Dictionary holding my emails and a unique key (this is the email que
- A FillQue Method, which populates the dictionary
- A ProcessQue method, which is a background thread. It checks the que, and SendAsycs any email in the que.
- A SendCompleted delegate which removes the email from the que. And calls FillQue again.
I've a few problems with this setup. I think I've missed the boat with the background thread, should I be spawning one of these for each item in the dictionary? How can I get the thread to 'hang around' for lack of a better word, if the email que empties the thread ends.
final update
I've put a 'while(true) {}' in the background thread. If the que is empty, it waits a few seconds and tries again. If the que is repeatedly empty, i 'break' the while, and the program ends... Works fine. I'm a bit worried about the 'while(true)' business though..
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
我会将所有消息添加到队列中,然后生成 10 个发送电子邮件的线程,直到队列为空。 伪 C#(可能无法编译):
如果消息相同,并且只有多个收件人,只需将消息与收件人交换,然后将单个消息参数添加到 Send 方法。
I would add all my messages to a Queue, and then spawn i.e. 10 threads which sent emails until the Queue was empty. Pseudo'ish C# (probably wont compile):
If the message is the same, and just having many recipients, just swap the Message with a Recipient, and add a single Message parameter to the Send method.
您可以使用 .NET 计时器来设置发送消息的时间表。 每当计时器触发时,抓取接下来的 10 条消息并将其全部发送,然后重复。 或者,如果您想要一般的速率(每秒 10 条消息),您可以让计时器每 100 毫秒触发一次,并每次发送一条消息。
如果您需要更高级的调度,您可以查看调度框架,例如 Quartz.NET
You could use a .NET Timer to setup the schedule for sending messages. Whenever the timer fires, grab the next 10 messages and send them all, and repeat. Or if you want a general (10 messages per second) rate you could have the timer fire every 100ms, and send a single message every time.
If you need more advanced scheduling, you could look at a scheduling framework like Quartz.NET
这不是
Thread.Sleep()
可以处理吗?您认为后台线程可以在这里起到很好的作用,这是正确的。 基本上你想要做的是为此进程创建一个后台线程,让它以自己的方式运行,延迟等等,然后在进程完成时终止线程,或者无限期地保留它(将其变成 Windows 服务或类似的东西将是一个好主意)。
关于多线程的一些介绍可以在这里阅读(包括 Thread.Sleep!)。
可以在此处阅读有关 Windows 服务的精彩介绍。
Isn't this something that
Thread.Sleep()
can handle?You are correct in thinking that background threading can serve a good purpose here. Basically what you want to do is create a background thread for this process, let it run its own way, delays and all, and then terminate the thread when the process is done, or leave it on indefinitely (turning it into a Windows Service or something similar will be a good idea).
A little intro on multi-threading can be read here (with Thread.Sleep included!).
A nice intro on Windows Services can be read here.
简短回答
使用队列作为有限缓冲区,由其自己的线程处理。
长答案
调用 fill-queue 方法来创建电子邮件队列,限制为(例如)10 封。用前 10 封未发送的电子邮件填充它。 启动一个线程来处理队列 - 对于队列中的每封电子邮件,异步发送。 当队列为空时,休眠一段时间,然后再次检查。 让完成委托从队列中删除已发送或有错误的电子邮件并更新数据库,然后调用 fill-queue 方法将更多未发送的电子邮件读入队列(备份到限制)。
您只需要围绕队列操作进行锁定,并且只需管理(直接)一个线程来处理队列。 您永远不会同时有超过 N+1 个线程处于活动状态,其中 N 是队列限制。
Short Answer
Use a queue as a finite buffer, processed by its own thread.
Long Answer
Call a fill-queue method to create a queue of emails, limited to (say) 10. Fill it with the first 10 unsent emails. Launch a thread to process the queue - for each email in the queue, send it asynch. When the queue is empty sleep for a while and check again. Have the completion delegate remove the sent or errored email from the queue and update the database, then call the fill-queue method to read more unsent emails into the queue (back up to the limit).
You'll only need locks around the queue operations, and will only have to manage (directly) the one thread to process the queue. You will never have more than N+1 threads active at once, where N is the queue limit.
我相信你的 hacky 解决方案实际上会起作用。 只需确保在递增和递减计数器的位周围有一个锁定语句:
I believe your hacky solution actually would work. Just make sure you have a lock statement around the bits where you increment and decrement the counter: