C# 在抛出错误之前尝试运行任务n次
我的方法是使用smtp服务器发送电子邮件。使用C# 在抛出错误之前尝试运行任务n次,c#,async-await,task-parallel-library,.net-4.5,C#,Async Await,Task Parallel Library,.net 4.5,我的方法是使用smtp服务器发送电子邮件。使用Task.Factory我调用该方法以不阻止UI: Task.Factory.StartNew(() => SendMail("mail@example.com", "Test title", "TEST body"), CancellationToken.None, TaskCreationOptions.None, TaskScheduler.Default) .ContinueWith(p => {
Task.Factory
我调用该方法以不阻止UI:
Task.Factory.StartNew(() => SendMail("mail@example.com", "Test title", "TEST body"), CancellationToken.None, TaskCreationOptions.None, TaskScheduler.Default)
.ContinueWith(p =>
{
if (p.IsFaulted)
{
if (p.Exception != null)
{
MessageBox.Show(p.Exception.ToString());
}
return;
}
MessageBox.Show("ok");
}, TaskScheduler.FromCurrentSynchronizationContext());
现在我想修改我的代码,以便在出现问题时能够尝试调用SendMail
10次。我已尝试使用do/while块,但无法使其正常工作:
private void button1_Click(object sender, EventArgs e)
{
bool success = false;
int i = 0;
int max = 10;
do
{
Task.Factory.StartNew(() => SendMail("mail@example.com", "Test", "TEST1"), CancellationToken.None, TaskCreationOptions.None, TaskScheduler.Default)
.ContinueWith(p =>
{
if (p.IsFaulted)
{
if (p.Exception != null)
{
MessageBox.Show(p.Exception.ToString());
}
return;
}
success = true;
MessageBox.Show("ok");
}, TaskScheduler.FromCurrentSynchronizationContext());
i++;
} while (!success && i < max);
if (!success)
{
MessageBox.Show("error");
}
else
{
MessageBox.Show("ok", "success", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
}
private void SendMail(string address, string title, string body)
{
Thread.Sleep(10000);
MailClient.Instance.Send(address, title, body);
}
private void按钮1\u单击(对象发送者,事件参数e)
{
布尔成功=假;
int i=0;
int max=10;
做
{
Task.Factory.StartNew(()=>SendMail(“mail@example.com,“测试”,“测试1”),CancellationToken.None,TaskCreationOptions.None,TaskScheduler.Default)
.ContinueWith(p=>
{
如果(p.IsFaulted)
{
如果(p.Exception!=null)
{
Show(p.Exception.ToString());
}
返回;
}
成功=真实;
MessageBox.Show(“ok”);
},TaskScheduler.FromCurrentSynchronizationContext());
i++;
}而(!success&&i
我想做的是能够在任务内部调用特定的方法,如果我得到异常,那么我想一次又一次地调用它,10次,如果在这10次之后它不会成功,我想显示异常。类似的东西可能会解决问题:
private int _maxAttempts = 10;
private void TrySendMail(int attemptNumber)
Task.Factory.StartNew(() => SendMail("mail@example.com", "Test title", "TEST body"), CancellationToken.None, TaskCreationOptions.None, TaskScheduler.Default)
.ContinueWith(p =>
{
attemptNumber++;
if (p.IsFaulted)
{
if (p.Exception != null)
{
if (_attempts < _maxAttempts)
{
// Try again
TrySendMail(attemptNumber);
}
else
{
MessageBox.Show(p.Exception.ToString());
}
}
return;
}
success = true;
MessageBox.Show("ok");
}, TaskScheduler.FromCurrentSynchronizationContext());
}
private int\u max=10;
专用void TrySendMail(int attemptNumber)
Task.Factory.StartNew(()=>SendMail(“mail@example.com,“测试标题”,“测试正文”),CancellationToken.None,TaskCreationOptions.None,TaskScheduler.Default)
.ContinueWith(p=>
{
attemptNumber++;
如果(p.IsFaulted)
{
如果(p.Exception!=null)
{
如果(_尝试次数<_最大尝试次数)
{
//再试一次
TrySendMail(attemptNumber);
}
其他的
{
Show(p.Exception.ToString());
}
}
返回;
}
成功=真实;
MessageBox.Show(“ok”);
},TaskScheduler.FromCurrentSynchronizationContext());
}
它不是最漂亮的,你要注意不要递归调用它太多次,从而导致堆栈溢出!十次就可以了
编辑:
我将尝试次数计数改为一个参数,以便在线程中更安全,以防您在线程中多次调用此发送邮件
编辑2:
上面提到的@mazharenko方法的实现可能如下所示:
private void TryAndRepeat(Action routine, int maxAttempts)
{
int attempts = 1 ;
bool success = false;
while (!success && attempts <= maxAttempts)
{
try
{
routine.Invoke();
success = true;
}
catch
{
if (attempts >= maxAttempts)
{
throw;
}
}
attempts++;
}
}
private void TryAndRepeat(操作例程,int-maxtures)
{
int=1;
布尔成功=假;
而(!success&&attempts=maxtempts)
{
投掷;
}
}
尝试++;
}
}
这有点离题,但每次我看到有人在IO绑定操作中使用线程时,我都会感到寒心:)
由于发送邮件是一项绑定到网络的操作,因此可以使用.NET 4.5中添加的等待操作
如果我可以接受JoelC发布的实现并对其进行重构:
private int _maxAttempts = 10;
private async Task TrySendMailAsync(int attemptNumber)
{
var smtpClient = new SmtpClient();
var mailMsg = new MailMessage("from@test.com", "to@test.com", "Test Subject", "Test Body");
while (!success && attempts <= maxAttempts)
{
try
{
await smtpClient.SendMailAsync(mailMsg)).ConfigureAwait(false);
success = true;
}
catch
{
if (attempts >= maxAttempts)
{
throw;
}
}
attempts++;
}
}
private int\u max=10;
专用异步任务TrySendMailAsync(int attemptNumber)
{
var smtpClient=新的smtpClient();
var mailMsg=新邮件消息(“from@test.com", "to@test.com“,”试验对象“,”试验机构“);
而(!success&&attempts=maxtempts)
{
投掷;
}
}
尝试++;
}
}
这将允许您根据请求进行循环,但让作为异步IO的主作业在不需要执行线程池线程的情况下工作。我没有阅读代码。我只想告诉你,我知道postsharp可以很容易地做到这一点,如果你想查看它的话。只需记下它在变量中被调用了多少次。如果计数小于10,则更新变量,否则抛出异常。@bikie我这样做了,但smtp客户端中出现错误,说我已经在调用方法
send
,除了调用我的代码后,我还收到error
消息。@vgSefa-我想避免购买额外的组件。Task.Factory.StartNew()=>TryAndRepeate(SendMail(…),10),…)
从自身调用同一个方法是否危险?唯一的危险是从自身调用同一个方法太多次会导致堆栈空间不足。我认为线程不应该引起问题,因为您在尝试发送邮件之后再次调用它,因为调用是在ContinueWith
中进行的。然而,mazharenko上面的评论可能暗示了一种更好的方法,即在单个任务中包含所有内容,而不是嵌套任务。