最近为了用smtp做一个发邮件的功能,折腾了好些时间,为了提醒自己以后不要再有类似的低级错误和加深对异步连接的理解,特地把这段痛苦的经历写下来。
首先,我参考了微软提供的smtp异步发送邮件的例程(下面是例程原文):
我自身的需求是,需要定时地发送邮件,于是我起初的做法大致如下:
这样做,乍一看,还真发现不了有什么错,但运行起来会发现,有时候会发送成功,但更多的时候会有system.net.mail.SmtpException、system.net.IOException等异常抛出,说是因为套接字(Socket)为空无法连接。
起 初还以为是微软的底层有问题,直到得到黄老板的提示,才知道,是因为在Socket还没发送完毕,因为异步发送client.SendAsync是无阻塞 的,Tick函数直接返回,这样那条临时线程就结束了,C#的垃圾回收机制,是延时回收的,但肯定是有快有慢,也就是有可能在IO结束前回收,也可能在 IO结束后回收,如果是结束后回收,那么什么事没有,否则,因为内存已经被回收,当然就抛出异常了。
所以,后来我把程序改成这样:
在线程结束前用邮件的发送完成状态加一个死循环,这样在IO完毕前,线程不会退出,这样问题就解决了。
回顾一下,整个错误的根源在于对异步连接和线程使用的理解不足,对于线程退出,垃圾回收机制发生作用也没有好的认识。
还有,要实现相同的功能,也可以改为用client.Send(message)同步连接函数。另外,这样实时创建线程,其实也不是好的做法,会让CPU花在线程切换上面的时间过多,影响效率,更好的做法是只创建1条Tick线程,然后用状态机管理其工作。